Мне очень нравится функционал, программирование, выкройки. React, Redux и mpj вдохновили мой стиль JavaScript, что привело к фантастическому опыту написания JS-приложений. На самом деле я не имею отношения ко всем головным болям, которые, как говорят люди, приходят с JavaScript, потому что я начал с дисциплины перспективы FP (честно говоря, я также начал JS, когда ES6 был в самом разгаре, поэтому стиль FP был прекрасен для написания).
При всех удовольствиях и преимуществах FP, которые я узнал в JS, трудно не импортировать некоторые из этих шаблонов в Ruby. Точно так же, как люди могут проводить время за решением судоку или кроссвордов, мне нравится решать задачи exercism.io на разных языках, поэтому я всегда сталкиваюсь с новыми возможностями написать свежий Ruby с функциональным уклоном.
Недавно я завершил реализацию алгоритма Луна на Ruby. Я сразу увидел в этом чистую функцию: взять текст и вернуть логическое значение (String -> Bool
). В более спартанских языках, таких как JS (с ES6) и чистых функциональных языках, это можно было бы сделать с очень небольшим количеством строк и символов, но самоуверенный рубист во мне и структура PORO (обычный объект Ruby) требуют немного больше шаблонного кода. и церемония.
Моя цель — написать Ruby функциональным способом, который по-прежнему уважает и прославляет идиомы моего любимого первого языка. Вот мой первый проход по алгоритму Луна:
Не беспокойтесь слишком о реализации. Просто знайте, что он опрашивает ввод строки и возвращает true
, если строка действительна, и false
, если нет.
Я был достаточно доволен решением, но мне не нравилось повторяющееся def method_name(raw)
. Но какой у меня был выбор, если я хочу написать чистые функции, которые принимают только входные данные и возвращают выходные данные?
Моя высокоуровневая цель — проверить правильность входной строки на основе различных наборов критериев: длины, типов символов и вычисленной суммы на основе спецификации Луна. В каждом методе, который принимает raw
в качестве входных данных, я только читаю его, чтобы вычислить какое-то новое значение. Что, если бы в Ruby был какой-то встроенный механизм, который позволял бы мне легко читать значения, не передавая их в методы?
Правильно! Друг каждого рубиста attr_reader
! Во второй итерации я использовал значение attr_reader
вместо передачи raw
всему и удалил класс singleton в пользу одного из моих любимых паттернов в Ruby.
Все мои приватные помощники выходят за пределы своей области действия для sanitized
, поэтому поначалу это кажется довольно нефункциональным. Но когда я действительно смотрю на эти функции, это частные методы, которые всегда будут возвращать одно и то же значение при одном и том же значении sanitized
. Таким образом, как если бы sanitized
был только что частично применен ко всем этим вспомогательным методам, и они просто поддерживают доступ к области замыкания. Я могу быть на 100% уверен в их поведении, пока sanitized
остается доступным только для чтения.
Хотя это намного больше кода, чем мне может понадобиться в реализации JS, я думаю, что он действительно хорошо читается. Это очень декларативно, что заставляет вас погружаться глубже, только если вы действительно этого хотите. Первоначально я использовал шаблон Enumerable#all?
, потому что ненавидел смотреть на
valid_length?(raw) && only_digits?(raw) && valid_sum?(raw)
Конечно, это не так уж и безумно, но необходимость столько раз повторять этот аргумент заставила меня чувствовать себя странно. Я думаю, что этот второй проход — хорошее сочетание классических Rubyisms и функциональной вспышки. Давайте рассмотрим два:
Рубизмы и элементы PORO
- Метод класса для экземпляра
def self.method; new.method; end
- Любимое
i
слово каждого рубиста:initialize
attr_reader
private
методы?
суффиксы для предикатных методов
Функциональные элементы
- Композиция/цепочка в
Luhn#luhn_sum
- Лямбды
->
в сочетании сEnumerable
методамиmap(&luhn_double)
- Переменная имена
x
🙀. Меня это устраивает, потому что я, очевидно, занимаюсь математикой, поэтому переменные, очевидно, являются математическими значениями (также известными как числа). Другими словами, я не думаю, что названиеvalue
,number
или дажеint
добавляет больше значения/намерения, чемx
. - Декларативный по умолчанию. Предполагается, что будущим разработчикам нужно знать только то, что делает
Luhn#valid?
, а не то, как он работает. Если разработчик хочет понять реализацию, он может это сделать, но не обязан. Было бы вполне достаточно просто взглянуть на определениеLuhn#valid?
, чтобы получить достаточно хорошее представление о том, что происходит.
Вывод
Как долго будут продолжаться мои поиски лучшего места функционального программирования и Ruby (fRuby?)? Кто знает. Но использование attr_reader
в качестве окольной формы частичного применения — приятный пример соблюдения соглашений Ruby и добавления очень дисциплинированного намерения, основанного на таких понятиях, как неизменность и чистота.
Руби интересно писать. Функциональный код, как правило, работает очень хорошо, и о нем легко рассуждать (учитывая понимание основных принципов). Разумный хорошо работающий код также доставляет удовольствие, поэтому я думаю, что есть много возможностей продолжать искать способы добавить функциональную изюминку сообществу Ruby.