了解单子函数的组成

时间:2019-08-05 04:46:43

标签: list haskell monads function-composition kleisli

我正在从“学习Haskell为伟大!”一书中学习单子。由Miran Lipovaca撰写。我试图理解单子的结合律。本质上,法律规定,当您拥有一串带有>>=的单子函数应用程序时,它们的嵌套方式无关紧要。

以下代码使人们能够将类型为a -> m b的函数的结果传递给类型为b -> m c的函数:

(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = (\x -> g x >>= f)

但是,对于以下示例:

ghci> let f x = [x, -x]
ghci> let g x = [x*3, x*2]
ghci> let h = f <=< g
ghci> h 3
[9, -9, 6, -6]

f xg x都起作用吗?看来它们是具有不同x值而不是函数的列表。第let h = f <=< g行在上面的代码中如何工作? fg必须是函数,因为它们与<=<一起使用,但是我不确定它们是什么。

2 个答案:

答案 0 :(得分:5)

f x = [x, -x]

这是普通的函数定义语法。我们通过写下应用到假设值f上的结果,来定义新功能。{p>

x(无论是语句还是let表达式)仅引入了一个块,您可以在其中进行定义,就像let ... in ...一样。定义本身使用与全局定义相同的语法。

如果您知道如何通过编写例如where放在文件中,则此语法完全相同(如果您不知道该怎么做,那么建议您在尝试理解Monadic函数组成之前通读一些基本的Haskell语法入门教程)。

因此,在这些定义之后,plusOne n = n + 1f是函数。 gf x并没有什么意义,因为您没有g x的应用范围。

如果您的作用域中确实有这样的值,那么x将是一个计算结果为列表的表达式,其中涉及调用函数f x。仍然不能说ff x是函数。

因此,现在应该很清楚,g x正在通过将let h = f <=< g运算符应用于h<=<来定义新值f

答案 1 :(得分:3)

没有什么比获得手动理解更好的了,就像在一张纸上手工研究定义一样。

f x = [x, -x]也可以写为f = (\ x -> [x, -x])。因此

  h 3 
= {- by def of h -}
  (f <=< g) 3 
= {- by def of (<=<) -}
  (\x -> g                   x >>= f               ) 3
= {- by defs of f and g -}
  (\x -> (\ x -> [x*3, x*2]) x >>= (\ x -> [x, -x])) 3
= {- by substitution -}
         (\ x -> [x*3, x*2]) 3 >>= (\ x -> [x, -x])
= {- by substitution -}
                 [3*3, 
                  3*2]         >>= (\ x -> [x, -x])
= {- by definition of (>>=) for [] -}
  concat [       (3*3)         &   (\ x -> [x, -x])  -- x & f == f x
         ,       (3*2)         &   (\ x -> [x, -x]) 
         ]
= {- by definition of concat -}
                 (3*3)         &   (\ x -> [x, -x])
         ++      (3*2)         &   (\ x -> [x, -x]) 
= 
  [9, -9, 6, -6]

edit )有关图片和这些 Kleisli箭头及其可组合性的更多讨论,请参见this older answer of mine