为什么&gt;&gt; =经常谈论而不是&lt; = <! - ?

时间:2017-08-04 18:06:55

标签: haskell

假设我们有g :: a -> bf :: b -> c。我们可以写:

  1. f . g :: a -> c
  2. 如果我们的函数返回monadic值(即上下文中的值),例如g1 :: (Monad m) => a -> m bf1 :: (Monad m) => b -> m c。我们可以写:

    1. f1 <=< g1 :: (Monad m) => a -> m c
    2. return x >>= g1 >>= f1,其中x :: a,以获取值。甚至是lambda \x -> return x >>= g1 >>= f1
    3. 在语法方面,似乎<=<.更为平行。 <=<让您更容易理解Monad只是关于保留上下文的函数组合。为什么>>=<=<更常被讨论?

2 个答案:

答案 0 :(得分:9)

<=<是解释monad laws的好方法:

 f <=< return = f -- right identity
 return <=< g = g -- left identity
 f <=< (g <=< h) = (f <=< g) <=< h -- associativity

它对于展示Kleisli箭的类别非常有用:

 newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
 instance Monad m => Category (Kleisli m) where
   Kleisli f . Kleisli g = Kleisli (f <=< g)
   id = Kleisli return

你会看到它出现在无点程序中。就个人而言,我也喜欢它的同伴=<<

虽然它更容易讨论monad法则和组合,但我认为仍然有一些强烈的教学理由{@ 1}}在monad教程和Haskell介绍中是首选。

第一个原因是>>=的强大功能是无点代码,并且对于大多数情况来说无点代码对来自C语法家族中的语言的人来说更难(C,C ++ ,Java,Python等)首先要了解。

如果“无点”是一个不熟悉的形容词,这里有三个相同功能的实现:

<=<

它们都运行相同的计算,但最后一个是所谓的无点样式,其中左侧的变量已通过eta conversion删除。

这个例子非常简单,但是无点样式很诱人,很容易导致初学者难以理解的代码。

另一个原因是初学者提出的一个近乎普遍的问题是 “我如何打开f a b = a + b * 2 f a = (a +) . (* 2) f = flip (.) (*2) . (+) 来获取IO String?”第一次面对时 Haskell的String monad。答案当然是,“你没有,你把其余部分联系起来 使用IO“计算,>>=可以很容易地解释这种关系 之间

>>=

putStrLn "Your first name: " >>= \_ ->
getLine >>= \first ->
putStrLn "Your last name: " >>= \_ ->
getLine >>= \last ->
putStrLn ("Hello " ++ first ++ " " ++ last)

当然,最后一个原因是do putStrLn "Your first name: " first <- getLine putStrLn "Your last name: " last <- getLine putStrLn ("Hello " ++ first ++ " " ++ last) >>=的定义中,并且 Monad不是,而这正是语言被定义的方式。人们是 更有可能在谈论类型成员而不是任意函数时 教别人关于类型类,特别是老师的时候 相对较新的主题本身(正如许多monad教程作者所做的那样)。

答案 1 :(得分:1)

虽然<=<对于许多应用程序来说更自然,但实际上>>=作为Monad类的主要功能有很好的实际原因。原因是,无论何时尝试撰写函数f :: a -> m bg :: b -> m c,您总是需要以某种方式解开f的结果,这正是>>=所做的!

比较Maybe

的定义
m >>= f = case m of
    Nothing -> Nothing
    Just a -> f a

f >=> g = \a -> case f a of
    Nothing -> Nothing
    Just b -> g b

尝试为>=>提供一个更简单的定义,这真的不可能!

一般来说,查看>>=>=>的默认定义,我们会看到:

(>>= f) = id >=> f
f >=> g = \a -> f a >>= g
-- equivalently = (>>= g) . (>>= f) . return

似乎>=>正在做更多工作&#34;比>>=,所以使用最简单的行为描述来定义Monad是明智的。

考虑这一点的理论方法是\m -> m >>= f正是id >=> f。我们所说的是>=>由其对身份的行为唯一定义。

(有人可能认为=<<更好,因为它与$<$><*>等函数应用的正常方向相匹配,但它并不是真的物质)