
В этом видео мы с вами разберем, что такое 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)
Если мы посмотрим в браузер, то код работает как и раньше. Как это все работает?
Как вы видите, кода стало существенно меньше из-за нашей вспомогательной функции 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.
Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.