1. Первые шаги
2. Сочетаем функции
3. Частичное применение (каррирование)
4. Декларативное программирование
5. Бесточечная нотация
6. Неизменяемость и объекты
7. Неизменяемость и массивы
8. Линзы
9. Заключение
Данный пост — это восьмая часть серии статей о функциональном программировании под названием "Мышление в стиле 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 читает значение линзы
- set обновляет значение линзы
- over применяет функцию трансформации к линзе
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 множество всего, что она предоставляет; в целом, достаточно для того чтобы делать большинство всех операций, которые мы совершаем в наших программах. Заключительная статья в этой серии делает обзор изученного и упоминает некоторые другие темы, которые мы можем пожелать исследовать самостоятельно.