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