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

时间:2012-09-08 10:37:24

标签: clojure monads

我需要帮助写一个'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   <...>
      ])

其中&lt; ...&gt;是功能。

2 个答案:

答案 0 :(得分: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

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

答案 1 :(得分:3)

domonad的输出必须是monadic值,在sequence-m的情况下,这意味着它必须是序列。要求nil的输出中断,而你没有monad。

你可能正在寻找的是使用monadic变换器直接在sequence-monad中添加“maybe”,这很容易做到并在这里描述:http://clojuredocs.org/clojure_contrib/1.2.0/clojure.contrib.monads/maybe-t

你想写

(def sequence-maybe-m (maybe-t sequence-m))

其中maybe-t将“maybe”添加到序列monad中。使用它将使

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

产量

(2 nil 4 3 nil 5 4 nil 6)

这是此类monad的有效输出。如果您需要取消其中包含nil的结果,只需在monad的输出上使用some nil?进行检查。

在您的示例中按照要求将nil绑定到b

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

也没有意义,因为nil不是序列。在转换后的monad中,返回值将是空列表()。将[nil]绑定到b更合适,然后你会获得(nil nil nil)

有助于记住monad用于组成相同签名的函数,并且它们本身可以是这种组合的一部分,因此它们必须产生monadic值(在本例中为序列)本身,并且在它们的体内任何绑定也必须具有一元价值。