Может ли эта do-monad быть заменена блоком let?
Автор здесь приводит следующий пример использования do-monad для объединения тестовых генераторов:
(require '[clojure.test.check.generators :as gen])
(require '[clojure.algo.monads :as m])
(m/defmonad gen-m
[m-bind gen/bind
m-result gen/return])
(def vector-and-elem
(m/domonad gen-m
[n (gen/choose 1 10)
v (gen/vector gen/int n)
e (gen/element v)]
[v, e]))
(gen/sample vector-and-elem)
([[0 -1 1 0 -1 0 -1 1] 0]
[[1 1 3 3 3 -1 0 -2 2] 3]
[[8 4] 8]...
Здесь комментатор утверждает, что это отличный пример монад не только ради них самих, но и для обеспечения реальной добавленной стоимости.
Мне это не кажется отличным от того, что делает блок let. Действительно - Брайан Марик здесь сравнивает домонаду с блоком let.
Мой вопрос: может ли эта do-monad быть заменена блоком let?
2 ответа
В контексте test.check
ответ нет, это do-monad
не может быть заменено let
блок. Но вы можете использовать gen/bind
а также gen/return
вручную, как это:
(def vector-and-elem
(gen/bind (gen/choose 1 10)
(fn [n]
(gen/bind (gen/vector gen/int n)
(fn [v]
(gen/bind (gen/elements v)
(fn [e]
(gen/return [v e]))))))))
Это то, что Monad
делает под прикрытием для вас.
Попытка написать это как let
:
(def vector-and-elem-let
(let [n (gen/choose 1 10)
v (gen/vector gen/int n)
e (gen/elements v)]
[v e]))
Не работает, потому что функции: choose
, vector
, а также elements
возвращает генератор, а не результат генератора. Так например gen/vector
ожидает Integer
в качестве второго аргумента не generator
и это let
даже не компилируется.
По состоянию на test.check 0.9.0
есть макрос gen/let
это поддерживает такие вещи.