Мышление в стиле Ramda: Линзы
- понедельник, 25 июня 2018 г. в 00:17:33
1. Первые шаги
2. Сочетаем функции
3. Частичное применение (каррирование)
4. Декларативное программирование
5. Бесточечная нотация
6. Неизменяемость и объекты
7. Неизменяемость и массивы
8. Линзы
9. Заключение
10. Использование Ramda с Redux
11. Функциональные компоненты с React stateless функциями и Ramda
12. Модульные редюсеры и селекторы
Данный пост — это восьмая часть серии статей о функциональном программировании под названием "Мышление в стиле Ramda".
В шестой и седьмой частях мы изучили, как читать, обновлять и трансформировать свойства объектов и элементы массивов в декларативном и иммутабельном стиле.
Ramda также предоставляет более общий инструмент для выполнения данных операций, который называется линзами.
Линза объединяет функцию-"геттер" и функцию-"сеттер" в один механизм. Ramda предоставляет набор функций для работы с линзами.
Мы можем думать о линзах как о чём-то, что фокусируется на определённой части большой структуры данных.
Основной способ создания линзы в Ramda — это функция lens. lens
принимает функцию-геттер и функцию-сеттер и возвращает новую линзу.
const person = {
name: 'Randy',
socialMedia: {
github: 'randycoulman',
twitter: '@randycoulman'
}
}
const nameLens = lens(prop('name'), assoc('name'))
const twitterLens = lens(
path(['socialMedia', 'twitter']),
assocPath(['socialMedia', 'twitter'])
)
Здесь мы используем методы prop
и path
как наши функции-геттеры, а также assoc
и assocPath
как наши функции-сеттеры.
Обратите внимание, что мы продублировали аргументы с названием свойства и путём к нужному свойству для этих функций. К счастью, Ramda предоставляет классные сокращения для наиболее распространённых ситуаций использования линз: lensProp, lensPath и lensIndex.
lensProp
создаёт линзу, которая фокусируется на свойстве объектаlensPath
создаёт линзу, которая фокусируется на вложенном свойстве объектаlensIndex
создаёт линзу, которая фокусируется на элементе массиваМы можем переписать наши вышесозданные линзы, используя lensProp
и lensPath
:
const nameLens = lensProp('name')
const twitterLens = lensPath(['socialMedia', 'twitter'])
Это намного проще и устраняет дубликаты. На практике, я нашёл, что мне практически никогда не нужна изначальная функция lens
.
Окей, прекрасно, мы создали пару линз. Что мы теперь можем делать с ними?
Ramda предоставляет три функции для работы с линзами.
view(nameLens, person) // => 'Randy'
set(twitterLens, '@randy', person)
// => {
// name: 'Randy',
// socialMedia: {
// github: 'randycoulman',
// twitter: '@randy'
// }
// }
over(nameLens, toUpper, person)
// => {
// name: 'RANDY',
// socialMedia: {
// github: 'randycoulman',
// twitter: '@randycoulman'
// }
// }
Обратите внимание, что set
и over
возвращают весь объект с изменённым значением, на котором была сфокусирована ваша линза.
Линзы могут пригодиться, если мы имеем определённую комплексную структуру данных, от которой мы желаем абстрагироваться при вызове кода. Вместо того чтобы предоставлять структуру или предоставлять геттеры, сеттеры и трансформеры для каждого доступного свойства, вместо этого мы можем предоставлять линзы.
Клиентский код далее может работать с нашими структурами данных через использование view
, set
и over
без связки с точной формой структуры данных
Теперь мы знаем о Ramda множество всего, что она предоставляет; в целом, достаточно для того чтобы делать большинство всех операций, которые мы совершаем в наших программах. Заключительная статья в этой серии делает обзор изученного и упоминает некоторые другие темы, которые мы можем пожелать исследовать самостоятельно.