Может ли эта 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 это поддерживает такие вещи.

Другие вопросы по тегам