Можно ли сделать 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:

  • читатель монада в ближайшем будущем
  • писательская монада в ближайшем будущем
  • государственная монада в ближайшем будущем
  • личная монада в ближайшем будущем
  • возможно, монада в ближайшем будущем
  • либо монада в замыкании
Другие вопросы по тегам