为Clojure映射和减少Monad ...... Juxt Monad怎么样?

时间:2012-03-26 23:45:47

标签: map clojure monads reduce

在学习Clojure的同时,我花了很多时间试图理解monad - 它们是什么以及我们如何使用它们......并没有太多的成功。然而,我发现了一个出色的“Monads for Dummies”视频系列 - http://vimeo.com/20717301 - 由Brian Marik为Clojure发布

到目前为止,我对monad的理解是它有点像一个宏,因为它允许一组语句以易于阅读的形式编写 - 但是monad更加正式化。我的观察仅限于两个例子:

1。 Identity Monad(或'let'monad)取自http://onclojure.com/2009/03/05/a-monad-tutorial-for-clojure-programmers-part-1/

我们希望撰写的表格是:

(let [a  1
      b  (inc a)]
  (* a b))

和相应的monad是

(domonad identity-m
    [a  1
     b  (inc a)]
 (* a b))

2。序列Monad(或'for'monad)取自http://onclojure.com/2009/03/06/a-monad-tutorial-for-clojure-programmers-part-2/

我们希望写的表格是:

(for [a (range 5)
      b (range a)]
  (* a b))

和相应的monad是

(domonad sequence-m
  [a (range 5)
   b (range a)]
  (* a b))

Clojure中的Monad定义

查看源代码,使用clojure monads库 - https://github.com/clojure/algo.monads

user=>(use 'clojure.algo.monads)
nil

indentity monad:

user=> (source identity-m)
(defmonad identity-m
  [m-result identity
   m-bind   (fn m-result-id [mv f]
              (f mv))
  ])

序列monad:

user=> (source sequence-m)
(defmonad sequence-m
   [m-result (fn m-result-sequence [v]
               (list v))
    m-bind   (fn m-bind-sequence [mv f]
               (flatten* (map f mv)))
    m-zero   (list)
    m-plus   (fn m-plus-sequence [& mvs]
               (flatten* mvs))
    ])

所以我的结论是monad是某种广义的高阶函数,它接受输入函数和输入值,添加自己的控制逻辑并吐出一个可用于'domonad'阻止。

问题1

最后,对于问题:我想学习如何写一个monad并说我想写一个'map monad'来模仿clojure中的'map'形式:

(domonad map-m
  [a [1 2 3 4 5]
   b [5 6 7 8 9]]
  (+ a b))

应该相当于

(map + [1 2 3 4 5] [5 6 7 8 9])

并返回值

[6 8 10 12 14]

如果我查看源代码,它应该给我一些类似于identity-m和sequence-m的代码:

user=> (source map-m)
(defmonad map-m
   [m-result ...
    m-bind   ...
    m-zero   ...
    m-plus   ...
    ])

问题2

我也希望能够定义'reduce-m'以便我可以写:

(domonad reduce-m
  [a [1 2 3 4 5]]
  (* a))

这可能会给我1 x 2 x 3 x 4 x 5 = 120或

(domonad reduce-m
  [a [1 2 3 4 5]
   b [1 2 3 4 5]]
  (+ a b))

会给我(1 + 2 + 3 + 4 + 5)+(1 + 2 + 3 + 4 + 5)= 30

最后 我是否也能够编写一个模仿juxt函数的'juxt monad',但是我传递了一组函数,而不是传递绑定值。 :

(domonad juxt-m
  [a #(+ % 1)
   b #(* % 2)]
  '([1 2 3 4 5] b a) )

给出

[ [2 2] [4 3] [6 4] [8 5] [9 6] ] 

潜在地,我可以用宏做所有这些事情,所以我真的不知道这些'monads'会有多么有用,或者它们甚至被认为是'monad'......拥有互联网上的所有资源,它在我看来,如果我想要正确学习Monads,我必须学习Haskell,现在,学习另一种句法形式太难了。我想我找到了一些可能相关的链接但对我来说太神秘了

请有人可以解释一下!

3 个答案:

答案 0 :(得分:9)

您的示例不是monad。 monad表示可组合的计算步骤。在琐碎的身份monad中,计算步骤只是表达式评估。在monad中,一步可能是成功或失败的表达。在序列monad中,step是一个表达式,它产生可变数量的结果(序列的元素)。在作者monad中,计算步骤是表达式评估和日志输出的组合。在状态monad中,计算步骤涉及访问和/或修改一段可变状态。

在所有这些情况下,monad plumbery负责正确组合步骤。 m-result函数打包“plain”值以适合monadic计算方案,m-bind函数将一个计算步骤的结果提供给下一个计算步骤。

在(map + a b)中,没有要组合的计算步骤。没有秩序的概念。它只是嵌套表达式评估。同样适用于减少。

答案 1 :(得分:2)

你的问题不是一种单子。它们看起来更像是一个语法糖,你可以使用宏完成,我甚至不建议你这样做,因为map,reduce等是简单的函数,没有必要使它们的接口复杂。

这些情况不是monad的原因是因为moands是一种包含正常值的放大值。在map和reduce的情况下,你使用的矢量不需要放大,以允许它在map中工作或减少。

在domoand表达式上尝试使用macroexpand会很有帮助。 例如:序列monad示例应扩展为:

(bind (result (range 5)) 
   (fn [a] 
      (bind (result (range a))
        (fn [b] (* a b))
   )))

其中bind和result是序列monad m-bind和m-result中定义的函数。 所以基本上在domand得到嵌套之后的向量表达式和向量之后的表达式被使用,因为在上面的情况下,(* ab)被原样调用(只是提供了a和b值)由monad)。在你的map monad示例中,矢量表达式应该是原样的,最后一个表达式(+ a b)应该以某种方式表示(map + a b),这不是monad应该做的。

答案 2 :(得分:2)

我找到了一些非常好的monad资源:

所以从Jim的指南 - http://www.clojure.net/2012/02/06/Legalities/ - 他给出了三个公理来定义'bind-m'和'reduce-m'函数:

  

<强>身份   第一个Monad法可以写成

     

(m-bind(m-result x)f)等于(f x)

     

这意味着无论m-result对x做什么使其成为monadic值,在将f应用于x之前m-bind撤消。因此关于m-bind,m-result有点像一个身份函数。或者在类别理论术语中,它的单位。这就是为什么有时你会看到它被命名为'unit'。

     

反向身份第二个Monad法可以写成

     

(m-bind mv m-result)等于mv,其中mv是monadic值。

     

这项法律类似于第一部法律的补充。它基本上确保m-result是一个monadic函数,无论m-bind对monadic值进行提取值,m-result都会撤消以创建monadic值。

     

<强>结合性   第三条Monad法可以写成

     

(m-bind(m-bind mv f)g)等于(m-bind mv(fn [x](m-bind(f x)g)))   其中f和g是monadic函数,mv是monadic值。

     

这条法律所说的是f是否应用于mv然后将g应用于结果,或者是否使用f和g的组合创建新的monadic函数无关紧要,然后将其应用于MV。无论哪种顺序,结果值都是相同的monadic值。换句话说,m-bind是左右联合的。

http://www.clojure.net/2012/02/04/Sets-not-lists/中,他给出了一个monad,它将集合作为输入而不是列表。将通过所有的例子......