¿Es posible hacer el Reader Monad de Haskell en Clojure?
monads reader-monad (2)
Por supuesto. Un Reader
es solo una función que toma un entorno y extrae algo de valor de él.
Con Reader
, m-result
toma algún valor y produce un lector que ignora el entorno y devuelve ese valor:
(defn reader-result
[value]
"Ignores environment, returns value"
(fn [env]
value))
m-bind
toma un lector y una función f
que acepta un valor y produce un nuevo lector. Luego combina esos argumentos para producir un nuevo lector que aplica el lector inicial a un entorno, alimenta el valor que produce para producir un nuevo lector y luego aplica ese lector al entorno:
(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))))
Con estas funciones, podemos definir Reader
con algo.monads
:
(m/defmonad Reader
[m-result reader-result
m-bind reader-bind])
Hay algunas funciones de ayuda importantes. run-reader
toma un lector y entorno y aplica el lector a ese entorno:
(defn run-reader
"Runs a reader against an environment,
returns the resulting environment"
[reader env]
(reader env))
Dado que nuestros lectores son solo funciones, run-reader
no es estrictamente necesario. Sin embargo, puede aclarar las cosas y nos mantiene más cerca de la implementación de Haskell, por lo que la usaremos para seguir adelante.
ask
y asks
vamos a examinar el entorno. ask
es un lector que devuelve el entorno. asks
toma un selector y crea un lector que aplica ese selector a un entorno:
(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)))
Esto nos lleva lo suficientemente lejos como para pasar por el primer ejemplo de 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)))
La otra función importante de Reader
es local
. Esto toma una función que modifica un entorno y un lector y crea un nuevo lector que modifica el entorno antes de pasarlo al lector original:
(defn local
[modify reader]
"A reader that modifies the environment
before calling the original reader"
(fn [env]
(run-reader reader (modify env))))
Con eso, podemos pasar por el segundo ejemplo :
(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)))
Entonces, eso es todo lo que se necesita para hacer Reader
.
He echado un vistazo al algo.monads y la documentación de Fluokitten . También he leído entradas de blog de mónada de Jim Duey , Konrad Hinsen y Leonardo Borges .
La única referencia que puedo encontrar al Reader Monad en Clojure es esta discusión de grupos de google .
Mi pregunta es: ¿es posible hacer el Reader Monad de Haskell en Clojure? Podría dar un ejemplo?
Hay algunos ejemplos fantásticos de las siguientes mónadas en Clojure aquí :
- el lector mónada en clojure
- el escritor mónada en clojure
- la mónada estatal en clojure
- la mónada de identidad en clojure
- la tal vez mónada en clojure
- la mónada en clojure