Мне очень нравится функционал, программирование, выкройки. 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.