Однажды я прошел курс функционального программирования.
Как я ни старался в то время, я не мог уложить это в голове. Сегодня мне было бы трудно написать что-нибудь связное на Haskell, я все еще не мог рассказать вам, что такое монада, или объяснить все тонкости других математических концепций, связанных с функциональным программированием.
Однако со временем я стал по-настоящему ценить подход, поощряемый парадигмами функционального программирования. Концепции часто приводят к коду, который намного легче анализировать, тестировать, составлять и адаптировать с течением времени.
У меня есть проект, над которым я работал годами. Один из тех проектов «когда-нибудь закончу». Я часто использую его, чтобы попробовать разные подходы и библиотеки, поскольку он достаточно сложен, чтобы сделать его интересным примером, и я не слишком беспокоюсь о том, когда закончу его. Иногда, как говорится, речь идет больше о путешествии, чем о пункте назначения!
В этом посте я расскажу о некоторых уроках, которые я усвоил, добавляя Ramda в этот проект, чтобы помочь с более функциональным подходом.
Ramda — это набор функций, который «облегчает создание функциональных конвейеров». С ним довольно легко начать, но через некоторое время я усвоил несколько уроков о том, как извлечь из этого максимальную пользу.
Если вам нужно больше пояснений по функциональному программированию, для этого есть множество других замечательных статей. Вот один пример:
«Адекватное введение в функциональное программирование
Эта статья является частью серии, в которой мы исследуем функциональное и реактивное программирование в целом... С тегом…dev. к"
Этот список библиотек и других ресурсов также стоит посмотреть:
Урок 1 — Названия функций Ramda могут сбивать с толку
У Ramda очень много функций, так что если вам нужно что-то сделать, наверняка для этого есть функция.
Однако мне было трудно найти то, что я искал, потому что некоторые названия казались немного неочевидными. Например, я нашел R.evolve полезным во многих случаях для применения различных преобразований к каждому свойству в объекте, но я нашел его только случайно.
Обязательно изучите документы, вы найдете несколько интересных инструментов!
Урок 2 — Будь проще
Вы просмотрели документы и нашли несколько интересных методов. Все эти методы отлично подходят для более сложной логики, но они также могут значительно усложнить понимание относительно простой части логики.
Давайте возьмем пример — скажем, вы хотите взять первый элемент из массива и вернуть этот элемент, если он больше 0, иначе вернуть undefined. С Ramda это может выглядеть примерно так:
R.ifElse( R.pipe(R.nth(0), R.gt(R.__, 0)), R.identity, R.always(undefined) );
С другой стороны, эквивалентная лямбда-функция может выглядеть примерно так:
([elem]) => elem > 0 ? elem : undefined;
Хотя все методы, которые предлагает Ramda, могут быть полезны, довольно легко в конечном итоге уставиться на код, который вы только что написали, пытаясь вспомнить, что же вы вообще пытались сделать.
Ramda помогает писать код функциональным образом, а не является единственным способом написания функционального кода.
Урок 3 — Нужно передать переменные на промежуточную стадию? Используйте лямбду!
Как только вы начнете писать более сложные конвейеры, вы в конечном итоге достигнете точки, где у вас есть этап, которому требуется доступ к параметру, переданному в начале конвейера.
Мы могли бы изменить предыдущие этапы, чтобы пройти через этот параметр. Однако теперь мы информировали их о неважных для них знаниях (новый параметр) и привязывали их именно к этому пайплайну.
Вместо этого мы могли бы обернуть каждый метод в адаптер:
const passThrough = (func) => ([arg1, arg2]) => ([func(arg1), arg2]); R.pipe( (arg1, arg2) => ([arg1, arg2]), passThrough(stage1), passThrough(stage2), stage3, stage4);
Это адаптирует предыдущие этапы, чтобы можно было удовлетворить потребности более позднего этапа, но без необходимости корректировать сам этап многократного использования.
Второй вариант, который я нашел, заключался в том, чтобы просто обернуть конвейер в лямбду:
(arg1, arg2) => R.pipe( stage1, stage2, stage3(arg2), stage4, )(arg1);
Хотя это и работает, но кажется немного хакерским, так как вводит состояние верхнего уровня. Тем не менее, я нашел это проще и быстрее, чем сквозной вариант выше. Какой подход имеет смысл, вероятно, будет зависеть от ситуации и ваших собственных (или вашей команды) предпочтений.
Они предназначены для моей справки, но я надеюсь, что эти уроки будут полезны всем, кто начинает работать с Ramda. Если вы нашли что-то полезное при начале работы с Ramda или если у вас есть вопросы/мысли по поводу вышеизложенного, дайте мне знать!