Это, безусловно, может быть сделано, но ключевым является то, что фразеологической реализация Haskell свободных монад основано на использовании системы типов для обеспечения определенного вида модульности и гарантии корректности. Используемые идиомы не могут быть идиоматическими в Clojure, и лучше всего придумать другую реализацию.
Просто посмотрите на полное определение Free
монады в Haskell:
data Free f r = Free (f (Free f r)) | Pure r
instance Functor f => Monad (Free f) where
-- Construct a "pure value" leaf node
return = Pure
-- Replace every "pure value" leaf node with the tree obtained by
-- applying `f` to its value. (Remember that Haskell is lazy, so this
-- tree is only created as it is consumed, and only those branches that are
-- in fact consumed.)
Pure a >>= f = f a
Free fa >>= f = Free (fmap (>>=f) fa)
Это использует следующий Haskell показывает:
- Тип занятия:
Functor
и Monad
- рекурсивные типы данных :
Free
является рекурсивным типом
- Полиморфизм более высокого сорта: примечание t hat переменная типа
f
в Free f r
фактически используется как конструктор типа . Это определение абстрагируется по типу контейнера при ограничении типа элемента.
Затем он также использует типа уровня фиксированной точки строительство, техника, которая не существует в Clojure. Самый простой вариант это такой тип:
newtype Fix f = Fix (f (Fix f))
Если вы когда-нибудь читали о Y combinator, вы можете думать об этом как аналог его, но на уровне типов, а не значений.
Но отложив все это, давайте сосредоточимся на существенной структуре здесь: свободная монада - это в основном своего рода лениво сгенерированная, возможно бесконечная древовидная структура. Functor
используется в свободной монады делает две вещи:
- Определить, какие типы узлов существуют в дереве, и какие данные осуществляется в каждом узле
- Разрешить общий код свободной монады для отображения функции по детей любого узла.
(Не попадайте в заблуждение о том, что смотрите на свободное монадическое дерево как «абстрактное синтаксическое дерево», а узлы дерева не представляют выражения или что-то в этом роде.)
Ядра универсального код свободной монады затем предоставляет две вещей:
- Тип
Pure
узла, который гарантированно существует в каждой свободной монаде. Эти узлы содержат только значение, а детей нет.
- Метод связывания, который лениво заменяет все листья
Pure
с результатом применения их значения к функции.
После того, как у вас есть это, предоставив подходящий функтор, вы можете использовать общий бесплатный код монады для создания ленивых деревьев в соответствии со структурой, которую предоставляет ваш функтор. Эти деревья - только инертные ценности; вы потребляете их, записывая функции интерпретатора, которые перемещаются по дереву в соответствии с какой-либо стратегией, чтобы получить нужные результаты. Но на самом деле, вы хотите, чтобы в вашей бесплатной библиотеке монадов были некоторые подходящие общие функции утилиты, которые помогут вам легче писать переводчики. Например:
-- | Generic recursive fold over a free monadic tree.
iter :: Functor f => (f a -> a) -> Free f a -> a
iter f (Pure a) = a
iter f (Free fa) = f (fmap (iter f) fa)
-- | If you can map any node to a corresponding action in another monad, you can map
-- the whole free monadic tree to a monadic action as well...
interpret :: (Functor f, Monad m) => (forall x. f x -> m x) -> Free f a -> m a
Так очевидный вопрос, если кто-то хочет, чтобы написать это в Clojure (или любой другой Lisp): почему бы один сделать это вместо того, чтобы просто писать DSL интерпретатор s-выражение?
Ну, одна вещь, которую дают монады, дает вам вид нормальных представлений монадических программ. Например, подумайте о следующих подобных обрывков Clojure и Haskell код:
;;; Clojure; these two expressions always produce the same result and
;;; have the same effects
(do a (do b c))
(do (do a b) c)
-- Haskell counterparts
(a >> b) >> c
a >> (b >> c)
S-синтаксис выражения в формах Clojure позволяет записывать два разных выражения, которые, тем не менее необходимы, чтобы всегда вести себя так же. В Haskell, с другой стороны, определение монады Free
гарантирует, что два выражения выше оценивают точно такое же значение -no программа может их отличить! Таким образом, вы можете написать интерпретатор s-exp с ошибкой или макрос, который обрабатывал эти две формы Clojure по-разному, но не так для свободных монадических деревьев.
Еще один ряд потенциальных преимуществ заключается в том, что свободные монады предоставляют некоторые стандартные методы для реализации языковых функций, таких как обратное отслеживание (используйте какой-то список в качестве вашего функтора, представляющий альтернативы в том порядке, в котором они должны учитываться) и приостановка/возобновление интерпретация программы (свободные монады тесно связаны с стилем продолжения прохождения).
Но для максимальной идиоматичности в Lisp вы, вероятно, захотите объединить оба метода: используйте s-exprs в качестве интерфейса, который вы переводите, используя некоторую общую библиотеку, в свободное представление монады в соответствии с предоставленными клиентом определениями специальные операции DSL. Предоставляйте также общие функции, чтобы упростить задачу клиента писать переводчики для их свободного монадического языка.
Я не знаю, существующего решения, но я думаю, что http://fluokitten.uncomplicate.org/codox/ и http://www.clojuresphere.com/cark/data.lenses бы места для начала, если вы хотите использовать существующую работу для создания чего-то подобного с минимальными усилиями. – noisesmith
Вы знаете, что ваш фрагмент кода - Haskell? Статья находится в Scala, хотя – jozefg
Спасибо @noisesmith - я думал что-то вроде этого http://timperrett.com/2013/11/21/free-monads-part-1/ - можете ли вы дать мне несколько указаний о том, с чего начать ? – hawkeye