Ramda с типами FP
Недавно я решил перейти с lodash на ramda, чтобы поиграть с функциональным способом составления моей логики. Я люблю это! После некоторого тщательного изучения FP я обнаружил, что речь идет не только о удобных чистых/бесплатных утилитах (ramda), но и о сложных (по крайней мере, для меня) математических абстракциях (fantasy-land). Я не понимаю всего этого, но шаблон «Либо и задача» выглядит очень удобным. Проблема в том, что я не уверен, как объединить его с утилитами ramda. Я знаю о ramda-fantasy, но она больше не поддерживается. Предлагаемые Ramda-fantasy библиотеки работают не так, как ramda-fantasy. Со всей этой новой информацией о типах Monads/Monoids/Functors я полностью потерялся.
Например, какое соглашение для этого?
Right('boo')
.map(x => x + '!')
.map(x => x.toUpperCase())
vs
R.pipe(
R.map(x => x + '!')
R.map(x => x.toUpperCase())
)(Right('boo'))
Мне не нужна рамда, если я решу полностью перейти на монады?
1 ответ
Один из способов подумать об этом — подумать о типах и функциях.
Ramda предлагает большой набор служебных функций. Они работают с массивами, объектами, функциями, строками, числами и т. д. Но они также работают с пользовательскими типами. Итак, в вашем примере
R.map
работает со всем, что соответствует спецификации Functor . Если реализация
Either
вы используете соответствие этой спецификации, тогда Ramda будет работать с ней.
Но Ramda не предоставляет типы . Он работает со встроенными типами, такими как Object, Array, Function и т. д. Но, возможно, вне
Lens
-- он не поставляет никаких собственных типов. Библиотеки, такие как Folktale, предоставляют большие коллекции типов, например
Maybe
,
Result
,
Validation
,
Task
а также ; более специализированные, такие как Fluture, предоставляют мощные версии одного конкретного типа (
Future
). Все эти типы реализуют спецификацию Functor. Очень неполный список таких реализаций предоставляет FantasyLand.
Эти два понятия, функции абстрактного типа и наборы типов, дополняют друг друга. Функция Ramda, которая работает с любым функтором, будет работать с любой используемой вами версией Либо (при условии, что она соответствует спецификации). Подробнее об этой взаимосвязи см. в этом StackOverflow Q+A.
В вопросе сравнивались эти два фрагмента:
Right('boo')
.map(x => x + '!')
.map(x => x.toUpperCase())
а также
R.pipe(
R.map(x => x + '!')
R.map(x => x.toUpperCase())
)(Right('boo'))
Но я бы тоже не подумал об этой проблеме с точки зрения Ramda. Ramda — это все о функциях . Он предоставляет функции и ожидает, что вы будете использовать их для создания более сложных функций, а затем использовать их для создания функций еще более высокого уровня.
Если бы мы написали эту функцию:
const bigBang = pipe(
map (x => x + '!'),
map (x => x .toUpperCase ())
)
или возможно эта версия
const bigBang = pipe (
map (concat (__, '!')),
map (toUpper)
)
Затем эта функция теперь доступна для использования на многих типах. Например:
bigBang (['boo', 'scared', 'you']) //=> ['BOO!', 'SCARED!', 'YOU!']
bigBang ({a: 'boo', b: 'ya'}) //=> {a: 'BOO!', b: 'YA!}
bigBang ((s) => s .repeat (2)) ('boo') //=> 'BOOBOO!'
bigBang (Right ('boo')) //=> Right ('BOO!')
bigBang (Left ('oops')) //=> Left ('oops')
bigBang (Just ('boo')) //=> Just ('BOO!')
bigBang (Nothing()) //=> Nothing ()
bigBang (Future ('boo')) //=> Future ('BOO!')
Первые три реализации — массив, объект и функция — предоставляются Ramda. Но остальные по-прежнему работают, поскольку Ramda взаимодействует со спецификацией FantasyLand Functor. И это будет работать, если вы предоставите свой собственный
map
метод на вашем типе (или даже лучше
fantasy-land/map
метод.)
Так что нет, вам не нужна Ramda для работы с монадами или другими абстрактными типами. Вы можете работать непосредственно с их реализацией. Но Ramda предлагает несколько хороших способов взаимодействия с ними в общем виде.