使用clojure.algo.monads编写序列 - 可能是monad

zca*_*ate 0 monads clojure

我需要帮助写一个'sequence-maybe-m'(一个monad,它结合了一个序列monad和一个monad的行为).

规则应该是:

  • 如果任何输入为nil,则整个表达式失败.
  • 否则,像monad序列那样评估身体.
    (domonad sequence-maybe-m [a [1 2 3] b [1 2 3]] (+ a b)) 
        ;; => (2 3 4 3 4 5 4 5 6)

    (domonad sequence-maybe-m [a [1 2 3] b nil] (+ a b)) 
        ;; => nil

    (domonad sequence-maybe-m [a [1 2 3] b (range a)] (+ a b)) 
        ;; => (1 2 3 3 4 5) same as 'for'

    (domonad sequence-maybe-m [a [1 2 3] b [1 nil 3]] (+ a b)) 
        ;; => nil

如果它与clojure.algo.monads库兼容,那将是一个奖励:

(defmonad sequence-maybe-m
     [m-result <...>
      m-bind   <...>
      m-zero   <...>
      m-plus   <...>
      ])
Run Code Online (Sandbox Code Playgroud)

其中<...>是函数.

Bey*_*mor 5

; helper function for nil-ness
(defn nil-or-has-nil? [xs] (or (nil? xs) (some nil? xs)))

; the actual monad
(defmonad sequence-maybe-m
          [m-result (fn [v] [v]) ; lift any value into a sequence

           m-bind (fn [mv f] ; given a monadic value and a function
                    (if (nil-or-has-nil? mv) ; if any nil,
                      nil ; result in nil
                      (let [result (map f mv)] ; map over valid input seq
                        (if (some nil? result) ; if any nils result
                          nil ; return nil
                          (apply concat result))))) ; else flatten resulting seq

           m-plus (fn [& mvs] ; given a sequence of mvs
                    (if (some nil-or-has-nil? mvs) ; if any nil,
                      nil ; result in nil
                      (apply concat mvs))) ; otherwise, join seqs

           m-zero []]) ; empty seq is identity for concatenation
Run Code Online (Sandbox Code Playgroud)

唯一的一点确实值得密切关注着这里是第二个nil-or-has-nil?m-bind.第一个是预期的 - 通过一个monadic值,m-bind必须确定它是否是nil-ish并且应该立即产生nil.第二个检查计算的结果 - 如果它失败(产生任何nil),那么整个结果必须是nil(而不是,例如,由此产生的空列表(apply concat [nil nil ...])).