В первой части этого поста мы говорили о том, как Free Monad может собрать монаду из Functor. Для этого вы можете создать программы, подобные DSL, и сгенерировать другую программу. Созданная вами программа становится простой структурой данных, представляющей логику предметной области. После того, как вы составили программу, вы можете соединить эти программы с интерпретатором. Вы можете переключить другой интерпретатор на dev и QA. Вы также можете абстрагироваться от реализации своей программы или оптимизировать ее, не меняя логику предметной области.

Мы начали с создания приложения Todo с этими алгебрами:

Во-первых, мы создали алгебру FlatMap и Pure и познакомились с созданием монадоподобных программ, обернув наши алгебры Todo в структуру данных «Free»:

Затем мы создали способ «поднять» эти алгебры в «свободную» монаду, создав неявное преобразование Action[A] => Free[Action, A]:

Наконец, мы закончили соединение программы и интерпретатора с нашими «Свободными» алгебрами, которые мы построили.

Примечание. Это краткий обзор из «Объясните Free Monad, как я пять (часть первая)». Я объясняю ход своих мыслей о том, как я пришел к этим выводам. Если вам интересно, ознакомьтесь с ним, прежде чем читать дальше.



Проблема

Приведенные выше решения, похоже, работают для создания программы с использованием Free Monad для приложения Todo. Однако это решение будет работать только для этого конкретного приложения. Мы не можем использовать нашу функцию runProgram или «поднять» наши алгебры до «бесплатной» версии. Будет довольно утомительно, если нам нужно реализовать runProgram для каждой программы, которую мы создаем, а также создавать неявное преобразование каждый раз, когда мы создаем новый набор алгебр.

Обобщающий подъем

Для обобщенного подъема нам просто нужно абстрагировать Action в конструктор типа F[_]:

Однако, если мы сделаем это, это будет стоить ошибки компиляции, потому что во многих случаях мы будем хотеть поднимать конструкторы конкретных типов вместо универсального «запечатанного типажа»:

Это вызовет ошибку for-comprehension, потому что он не может автоматически поднять значение до типа Action запечатанного.

Один из способов сделать это — создать «умный конструктор», сначала установив значение как общую алгебру Action и определив функцию, которая абстрагирует обычный ввод в «бесплатную» его версию.

Помните, что возврат типа «Free» определяется типом возврата обобщенной алгебры. Например, create вернет TodoAction[Todo], потому что алгебра для Create возвращает Action[Todo]:

case class Create(description: String) extends Action[Todo]

Затем мы можем построить нашу программу с помощью этих умных конструкторов:

Обобщение RunProgram

Ссылка на runProgram из предыдущей статьи:

Текущая программа запуска имеет функцию execute, связанную с функциями с Action. Кроме того, Free[Action,A] ограничено конкретным Todo приложением. Как мы можем обобщить это, чтобы взять любую программу F[_]?

Мы можем обобщить runProgram, взяв конструктор типа F[_], но как мы будем обобщать функцию execute?

Каким-то образом функция execute должна быть общей функцией, которую определяет пользователь при создании программы. Следовательно, это должно быть какое-то trait, которое нужно будет передать в качестве аргумента. Если вы внимательно посмотрите на реализацию выше, функция execute(fa) принимает F[A] и возвращает A. Таким образом, A может быть входом в функцию fn. Поэтому нам нужно определить функцию execute, которая принимает F[A] и возвращает A. Давайте определим функцию execute внутри типажа Executor:

Мы передаем класс признаков Executor в качестве аргумента в runProgram и вызываем execute внутри этого признака Executor:

Затем мы можем определить наш интерпретатор для приложения Todo в функции Executor, execute и передать значение в runProgram:

runProgram(program, executor)

Наконец-то мы можем запускать Free[F,A] в любых программах, предоставив пользовательский 'execute', который интерпретирует нашу алгебру. Однако можем ли мы обобщить это еще дальше?

Обобщающий исполнитель

Текущая функция Executor принимает F[A] и возвращает A. Однако эта функция ограничена типом чистого эффекта. Мы можем каким-то образом обобщить это, обобщив тип эффекта. Если вы не уверены в том, что означает эффект, ознакомьтесь с моей статьей Что такое «эффект или эффективный в функциональном программировании?»

Такой вид преобразования между функторами называется естественным преобразованием. Вместо сигнатуры F[A] => A, эквивалентной F[A] => Id[A], мы можем иметь F[A] => G[A], где G[_] может быть конструктором любого типа.

Давайте реорганизуем нашу функцию Executor, чтобы она принимала конструктор двух типов:

Затем runProgram возьмет дополнительный конструктор типа G[_]:

Вы заметили, что приведенный выше код не компилируется. Изначально функция execute возвращает A. Однако после изменения нашей функции Executor, чтобы она возвращала G[A], мы не можем передать результат функции f в FlatMap.

Мы можем решить эту проблему, потребовав, чтобы G была монадой, и использовать flatMap для привязки значения внутри G к f:

Вывод

В этой статье мы узнаем, как еще больше обобщить наше приложение, обобщив нашу «бесплатную» оболочку, чтобы использовать ее в различных приложениях.

Во-первых, мы обобщаем функции lift и вводим «умный конструктор» для построения нашей программы. Затем мы обобщили наш runProgram, интерпретатор, который запускает программу, отправив трейт Executor. Наконец, мы еще больше обобщили наш признак Executor, введя естественную трансформацию в наш признак Executor.

Структура Free существует в Cats библиотеке под разными именами. Надеюсь, вы понимаете концепцию Free и то, как Free работает. Есть также Free Applicative, и разница заключается в runProgram и его Free алгебре, где вместо того, чтобы быть монадой, это аппликативная функциональность.

Весь исходный код этой статьи можно посмотреть здесь.

Источник и ссылка: Объяснение бесплатных монад (часть 1). Создание составных DSL | Алексей Авраменко | MediumКошки: FreeMonads

Спасибо, что прочитали! Если вам понравился этот пост, не стесняйтесь подписаться на мою рассылку, чтобы получать уведомления о эссе о карьере в технологиях, интересных ссылках и контенте!

Вы можете подписаться на меня, а также подписаться на Medium, чтобы получать больше подобных сообщений.

Первоначально опубликовано на https://edward-huang.com.