На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Модераторы: ElcnU, ANDLL, fatalist
  
> MonadDemo, Functional vs Imperative
    Предлагаю вашему вниманию демо-пример, где четко :D показано различие фунционального от императивного стиля программирования. Первая часть кода содержит императивный подход, а вторая - функциональный с использованием монады Maybe:
    ExpandedWrap disabled
      class Person {
          constructor(name, age) {
              this._name = name;
              this._age = age;
          }
          get name() {
              return this._name;
          }
          get age() {
              return this._age;
          }
      }
       
      // data
      const persons = [
          new Person('Gregory', 42),
          new Person('Julia', 27),
          new Person('Mark', 30)
      ];
       
      // imperative
      function findPerson(persons, name) {
          for (const person of persons) {
              if (person.name === name) {
                  return person;
              }  
          }
          throw Error('Person not found.');
      }
       
      try {
          var p = findPerson(persons, 'Gregory');
          console.log(p.name);    
      } catch (error) {
          console.log(error.message);
      }
       
      // functional
      const R = require('ramda');
      const Maybe = require('ramda-fantasy').Maybe;
       
      const findPerson2 = (persons, name) => R.compose(
          person => Maybe.toMaybe(person),
          (persons, name) => persons.filter(p => p.name === name)[0]
      )(persons, name);
       
      var p = findPerson2(persons, 'Julia');
      console.log(p.map(R.prop('name')).getOrElse('Person not found.'));

    Чтобы не писать ручками все функциональные приблуды :D, заюзал библу Ramda и ramda-fantasy.
    Сообщение отредактировано: Cfon -
    "What I cannot create, I don't understand" Richard Feynman.
    https://github.com/Cfon/
    :D
      Дальше ожидается "и ..." :)
      Мои программные ништякиhttp://majestio.info
        Цитата JoeUser @
        Дальше ожидается "и ..." :)

        чуть позже запосщу детальное обсуждения фунционального подхода.
        "What I cannot create, I don't understand" Richard Feynman.
        https://github.com/Cfon/
        :D
          подравил код findPerson, поскольку R.compose возвращает функцию, параметрами которой являются параметры последней переданой функции, то ее обертывание не требуется. Также выделил операцию взятие по индексу в отдельную функцию, тем самым улучшив ясность кода:
          ExpandedWrap disabled
            const findPerson = R.compose(
                person => Maybe.toMaybe(person),
                persons => persons[0],
                (persons, name) => persons.filter(p => p.name === name)
            );

          как видите findPerson состоит из трех последовательных вызовов функций, отсчет начинается снизу, последний вызов вернет результат в виде монады. Заметьте вызов этих функций произойдет тока когда будет обращение к собствено findPerson.
          На этом строиться вся логика функционального программирования, т.е. мы составляем нужный функционал из коротких функций, которые легко читать и понимать. Эти функции в свою очередь являются чистыми, т.е. без побочных эффектов, или еще их называют ссылочно-прозрачными, это когда результат функции всегда зависит от его входных параметров и не меняется. Это приводит к тому что их легко тестировать. Вооот :D
          Сообщение отредактировано: Cfon -
          "What I cannot create, I don't understand" Richard Feynman.
          https://github.com/Cfon/
          :D
            Предыдущий пример можно еще сократить, поскольку Maybe.toMaybe принимает один параметр, ее можно сразу передать в compose:
            ExpandedWrap disabled
              const findPerson3 = R.compose(
                  Maybe.toMaybe,
                  persons => persons[0],
                  (persons, name) => persons.filter(p => p.name === name)
              );


            - Что такое Maybe?
            Maybe это такая клевая штукенция :D, которая позволяет отложить обработку ошибок на момент когда это потребуется без генерации исключений. К примеру вот как это выглядит в случае исключений без Maybe
            ExpandedWrap disabled
              const findPerson = R.compose(
                  person => { //<-- не чистая функция
                      if (!person) throw Error('Person not found.')
                      return person;
                  },
                  persons => persons[0],
                  (persons, name) => persons.filter(p => p.name === name)
              );
               
              try {
                  var p = findPerson(persons, 'Julia');
                  console.log(p.name);
              } catch (error) {
                  console.log(error.message);
              }

            Как видите вызов findPerson в случае если объект не обнаружен выбрасывает исключение, которое надо обработать иначе программа прервет выполнение. Также стоит отметить, что фукнция которая вызывает исключение не является чистой, по понятной причине. В случае Maybe вы просто вызываете findPerson, заключать вызов в try-catch не требуется и что самое интересное обработать результат этой функции можно когда угодно:
            ExpandedWrap disabled
              var p = findPerson(persons, 'Gregory');
              //... какой то еще код...
              var result = p.map(R.prop('name')).getOrElse('Person not found.');
              //=> Gregory

            Да конечно можно и не выбрасывать исключение и просто возвращать объект ошибки:
            ExpandedWrap disabled
              const findPerson = R.compose(
                  person => { //<-- не чистая функция
                      if (!person) return { error: 'Person not found.' }
                      return person;
                  },
                  persons => persons[0],
                  (persons, name) => persons.filter(p => p.name === name)
              );
               
              var p = findPerson(persons, 'Julia');
              //... какой то еще код...
              if (!p.error) {
                  console.log(p.name);
              } else {
                  console.log(p.error);
              }

            но в это случае нам всеравно придется писать код обратотки ошибочного условия, что приводит как спагетти-коду читай гавнокод :D

            - Что означает p.map(R.prop('name')?
            map - тоже что и в случае Array.prototype.map :D отображает, переданную функцию на данные и возвращает новый объект Maybe.
            R.prop - это Ramda функция, возвращает функцию, которая при предоставлении объекта возвращает указанное свойство этого объекта, если оно существует. В нашем случае возвращает функцию вида val => val.name.

            - А нельзя сразу обратиться к свойсвту name объекта?
            Можно например p.value.name

            - Зачем тогда так p.map(R.prop('name').getOrElse('Person not found.') писать?
            Затем что если объект не обнаружен, то вот это p.value.name приведет к исключению TypeError обращение к свойству name не существующего объекта (undefined).

            - А зачем конецовой getOrElse('Person not found.')? :huh:
            :wall: :blink: :D
            Как я уже писал выше map возвращает новый объект Maybe его еще называют монада.
            Так вот это вот монада буть она не ладна! :D хранит наше значение, в нашем случае значение name и чтобы корректно получить его, а не через p.value.name (хотя так тоже можно) юзается getOrElse, который вернет в зависимости от исхода map либо name, либо строку, переданную через параметр, в нашем случае это 'Person not found.'

            - А что такое монада? :huh:
            Это еще одно модное словечко, которое стоит запомнить и сувать его везде где тока можно, чтобы показать что ты не джун, а сеньор JS :D
            Ну а если серьезно то монада это математический термин из теории категорий, что он там обозначает я не интерисовалси, а в программировании это тип данных, со специфическим фейсом :D
            Сообщение отредактировано: Cfon -
            "What I cannot create, I don't understand" Richard Feynman.
            https://github.com/Cfon/
            :D
            1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
            0 пользователей:


            Рейтинг@Mail.ru
            [ Script Execution time: 0,0842 ]   [ 14 queries used ]   [ Generated: 18.11.19, 03:08 GMT ]