#2 Каррирование (currying) в Javascript

poster
В этом видео мы с вами разберем, что такое currying (или по русски каррирование) и зачем оно нужно в javascript.
Понравилось? Поделитесь с друзьями!
Понравилось?
Поделитесь с друзьями!
Комментарии
Текст видео

В этом видео мы с вами разберем, что такое currying (или по-русски каррирование) и зачем оно нужно в javascript.

Главная идея currying в том, чтобы писать более чистый и элегантный код.

Обычно, в javascript мы описываем функцию вот так:

var add = function (a, b) {
  return a + b
}

add(1, 2)

То есть у нас обычная функция, которая принимает 2 аргумента и складывает их.

Ее можно вызвать с большим количеством аргументов

add(1, 2, 'ignore')

и все, по-прежнему, будет работать, так как третий аргумент игнорируется.

С меньшим количеством аргументов вызов данной функции вернет нам NaN:

add(1)

Каррируемая функция же отличается тем, что аргументы в нее передаются в виде функций с 1 аргументом.

У меня есть html страничка, где подключен файл с curry функцией и нашим кодом.

Давайте попробуем карировать наш метод add

var add = function (a, b) {
  return a + b
}

var curriedAdd = curry(add)

console.log(curriedAdd(1))

Если мы попробуем вызвать его с одним аргументом, то мы увидим, что он возвращает нам функцию.

Если же мы добавим еще 1 аргумент, то метод вызовется как обычно.

console.log(curriedAdd(1, 2))

Обратите внимание, что мы можем вызвать метод и по другому.

console.log(curriedAdd(1)(2))

И все будет работать по прежнему.

Что же это нам дает и в чем же плюсы curry?

Первый плюс - это то, что функции теперь можно разбивать на маленькие кусочки, которые очень легко переиспользовать.

Часто встречающаяся задача - из массива обьектов вытащить id из каждого элемента и собрать их в новый массив.

На обычном javascript мы бы написали вот так

var objects = [
  {
    id: 1
  },
  {
    id: 2
  },
  {
    id: 3
  }
]

objects.map(function (object) {
  return object.id
})

Но этот код не лаконичный и мы можем его улучшить.

С помощью карри мы можем создать специальную вспомогательную функцию get, которая при вызове будет возвращать нам поле обьекта.

var get = curry(function (property, object) {
  return object[property]
})

Теперь мы можем переписать наш код на такой вариант

var get = curry(function (property, object) {
  return object[property]
})

var result = objects.map(get('id'))

console.log(result)

Если мы посмотрим в браузер, то код работает как и раньше. Как это все работает?

  1. get('id') является частично настроенной get функцией. То есть, мы указали ей первый аргумент property - id. Если ее вызвать без ничего, то она просто вернет функцию, так как ей передано недостаточно аргументов, чтобы она вернула результат.
  2. Когда мы передаем ее в map, то get функция вызывается полностью, передавая каждый object вторым аргументом.

Как вы видите, кода стало существенно меньше из-за нашей вспомогательной функции get, которая каррирована и умеет получать проперти обьекта.

Теперь было бы хорошо создать функцию, которая бы получала ids из массива обьектов. На чистом javascript мы бы написали это как то так

var get = curry(function (property, object) {
  return object[property]
})

var getIds = function (objects) {
  return objects.map(get('id'))
}

console.log(getIds(objects))

То есть getIds у нас принимает массив обьектов на вход и возвращает map вызывая внутри функцию get('id').

Но с curry этот код можно существенно улучшить. Что если мы каррируем метод map?

var map = curry(function (fn, values) {
  return values.map(fn)
})

Наш каррированый метод map будет принимать функцию первым аргументом, а значения вторым аргументом.

И теперь мы можем значительно упростить наш код

var get = curry(function (property, object) {
  return object[property]
})

var map = curry(function (fn, values) {
  return values.map(fn)
})

var getIds = map(get('id'))

console.log(getIds(objects))

Если мы посмотрим в браузер, то все работает, как и раньше.

Теперь видно, что когда мы строим код на маленьких блоках из каррированных функций, то мы можем легко добавлять этим методам различные возможности.

Давайте представим, что мы получаем данные от сервера.

У нас есть метод fetchFromServer, который возвращает нам промис. Но мы просто сэмулируем его

var fetchFromServer = function () {
  return new Promise(function (resolve) {
    resolve({
      user: 'jack',
      posts: [
        {
          title: 'why curry?'
        },
        {
          title: 'functional programming'
        }
      ]
    })
  })
}

Как бы мы написали код на чистом javascript, чтобы получить массив title?

fetchFromServer()
  .then(function (data) {
    return data.posts
  })
  .then(function (posts) {
    return posts.map(function (post) {
      return post.title
    })
  })
  .then(function (titles) {
    console.log('titles', titles)
  })

Этот код можно сделать намного читабельнее с функциями map и get, которые мы создали ранее.

fetchFromServer()
  .then(get('posts'))
  .then(map(get('title')))
  .then(function (titles) {
    console.log('titles', titles)
  })

В первом then мы просто хотим получить поле из обьекта. Во втором - сделать map и получить из каждого поля title.

Как вы видите, curry дает нам фантастические возможности использования функционального программирования в javascript.

Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.

Только зарегистрированные пользователи могут оставлять комментарии.  Войдите, пожалуйста.
fruity4pie
10 месяцев назад
А каким образом из map, object идет вторым аргументом в get ?? Не совсем ясно(
dk
2 лет назад
сорри ответ похоже в след уроке. Хорошо бы было на этой страничке подключить curry.js
monsterlessons
2 лет назад
В исходном коде урока она подключается к index.html https://github.com/monsterlessons/currying-in-javascript В следующем уроке мы учимся ее писать с нуля.
dk
2 лет назад
Добрый вечер. А самой функции curry почему нет? В видео такая используется? : function curry(func, a) { return function (b) { return func(a, b); }; }
Aleksandr Polyakh
2 лет назад
Доброе время суток. Очень приятно смотреть ваши видео, видно, что Вы опытный специалист, скажите пожалуйста какие книги по JS вы прочитали ?
monsterlessons
2 лет назад
Добрый день. Спасибо за отзыв. Лет 5 назад я перечитал почти все популярные книги по JS, но не могу сказать, что они мне очень много дали или порекомендовать какую-то конкретную. Я предпочитаю читать статьи, новости и исходный код библиотек.
Alexey Solomko
2 лет назад
Очень крутой пример, сколько раз читал про каррирование, никак не мог найти ему применение на практике, спасибо за хороший пример!
monsterlessons
2 лет назад
Спасибо. Я тоже нормальных туториалов не нашел, поэтому решил снять.