问题
The author here provides the following example usage of a do-monad to combine test generators:
(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]...
There commentator here asserts that this is a great example of monads not just for their own sake, but for providing a genuine value-add.
To me this doesn't seem that different from what a let-block is doing. Indeed - Brian Marick here compares the do-monad to a let block.
My question is: Could this do-monad be replaced by a let block?
回答1:
In the context of test.check
the answer is no, this do-monad
can't be replaced by a let
block. But you can use the gen/bind
and gen/return
manually like this:
(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]))))))))
This is what the Monad
is doing under the covers for you.
Trying to write this as a let
:
(def vector-and-elem-let
(let [n (gen/choose 1 10)
v (gen/vector gen/int n)
e (gen/elements v)]
[v e]))
Doesn't work because the functions: choose
, vector
, and elements
returns a generator not the result of the generator. So for example gen/vector
expects a Integer
as the second argument not a generator
and this let
doesn't even compile.
回答2:
As of test.check 0.9.0
there is a macro gen/let
that supports this sort of thing.
来源:https://stackoverflow.com/questions/28961373/could-this-do-monad-be-replaced-by-a-let-block