Можно ли сделать Reader Monad из Haskell в Clojure?
Я взглянул на документацию algo.monads и fluokitten. Я также прочитал записи блога монады Джима Дуэя, Конрада Хинсена и Леонардо Борхеса.
Единственная ссылка, которую я могу найти для Монады Читателя в Clojure, - это обсуждение групп Google.
Мой вопрос: возможно ли сделать Read Monad из Haskell в Clojure? Не могли бы вы привести пример?
2 ответа
Конечно. Reader
это просто функция, которая берет окружение и извлекает из него некоторую ценность.
С Reader
, m-result
принимает некоторое значение и создает читателя, который игнорирует окружение и возвращает это значение:
(defn reader-result
[value]
"Ignores environment, returns value"
(fn [env]
value))
m-bind
берет читателя и функцию f
который принимает значение и производит нового читателя. Затем он объединяет эти аргументы для создания нового считывателя, который применяет начальный считыватель к среде, передает полученное значение f
создать новый читатель, а затем применить этот читатель к среде:
(defn reader-bind
[reader f]
"Applies reader to environment,
then applies f to new environment"
(fn [env]
(let [read-value (reader env)]
((f read-value) env))))
С помощью этих функций мы можем определить Reader
с algo.monads
:
(m/defmonad Reader
[m-result reader-result
m-bind reader-bind])
Есть несколько важных вспомогательных функций. run-reader
берет читателя и среду и применяет читателя к этой среде:
(defn run-reader
"Runs a reader against an environment,
returns the resulting environment"
[reader env]
(reader env))
Поскольку наши читатели просто функции, run-reader
не обязательно Однако, это может прояснить ситуацию и приблизить нас к реализации Haskell, поэтому мы будем использовать ее в дальнейшем.
ask
а также asks
давайте рассмотрим окружающую среду. ask
это читатель, который возвращает окружающую среду. asks
берет селектор и создает читателя, который применяет этот селектор к среде:
(defn ask
"A reader that returns the environment"
[env]
env)
(defn asks
"A reader that returns the result of
f applied to the environment"
[f]
(fn [env]
(f env)))
Это делает нас достаточно далеко, чтобы пройти первый Reader
пример:
(defn lookup-var
[name bindings]
(get bindings name))
(def calc-is-count-correct?
(m/domonad Reader
[binding-count (asks #(lookup-var "count" %))
bindings ask]
(= binding-count (count bindings))))
(defn is-count-correct?
[bindings]
(run-reader calc-is-count-correct? bindings))
(def sample-bindings {"count" 3, "1" 1, "b" 2})
(println
(str "Count is correct for bindings " sample-bindings ": "
(is-count-correct? sample-bindings)))
Другой важный Reader
функция local
, Это берет функцию, которая изменяет среду и считыватель, и создает новый считыватель, который изменяет окружение, прежде чем передать его исходному считывателю:
(defn local
[modify reader]
"A reader that modifies the environment
before calling the original reader"
(fn [env]
(run-reader reader (modify env))))
С этим мы можем пройти второй пример:
(def calc-content-len
(m/domonad Reader
[content ask]
(count content)))
(def calc-modified-content-len
(local #(str "Prefix " %) calc-content-len))
(let [s "12345"
modified-len (run-reader calc-modified-content-len s)
len (run-reader calc-content-len s)]
(println
(str "Modified 's' length: " modified-len))
(println
(str "Original 's' length: " len)))
Итак, это все, что нужно, чтобы сделать Reader
,
Здесь есть несколько фантастических примеров следующих монад в Clojure:
- читатель монада в ближайшем будущем
- писательская монада в ближайшем будущем
- государственная монада в ближайшем будущем
- личная монада в ближайшем будущем
- возможно, монада в ближайшем будущем
- либо монада в замыкании