试图了解Monads。 >>操作者

时间:2014-07-20 05:57:35

标签: haskell monads operator-keyword

我是haskell的新手,我正在通过LearnYouAHaskell学习。我无法理解(>>)运算符背后的原因。
默认实现是:

(>>) :: (Monad m) => m a -> m b -> m b  
m >> n = m >>= \_ -> n 

这是(据我所知)忽略第一个值并返回第二个值。但是,从LearnYouAHaskell的例子中可以看出:

  

ghci的>什么>>只是3
  没有
  ghci中>只是3>>没有
  没有

所以它忽略第一个值。然而,通过一些研究,我发现了here

的引用
  

>>函数绑定运算符忽略其第一个操作的值   并且仅作为整体结果返回其第二个动作的结果。

所以我困惑关于此运算符的使用情况,我想问两件事:

  1. 它实际上做了什么
  2. 何时有用?

5 个答案:

答案 0 :(得分:15)

>>函数只会忽略第一个值的结果,但不会忽略第一个值的副作用。要了解您的示例,请参阅如何定义Maybe Monad:

instance Monad Maybe where
  return = Just
  (Just x) >>= f = f x
  Nothing >>= _ = Nothing

>>函数的定义如下:

m >> k      = m >>= \_ -> k
根据{{​​1}} monad的定义,

Nothing >> _将生成Nothing。在第二个示例中,Maybe扩展为Just 3 >> Nothing并生成Just 3 >>= \_ -> Nothing。为了举例说明它如何忽略第一个动作的值但不忽略副作用,请考虑以下示例:

Nothing

你可以在上面的例子中看到,虽然它忽略了λ> print 3 >> print 4 3 4 print 3的结果,但它没有忽略它的副作用,即显示{{1}到屏幕。

一旦开始使用其他Haskell库,

()函数就会变得有用。偶尔使用它们的两个地方是处理Parsers(parsec,attoparsec)和Pipes库。

答案 1 :(得分:8)

它忽略了第一个动作的值,而不是动作本身。

Just 3 >> Just 5

操作Just 3的值为3。它在\_ -> n部分被忽略。总体结果为Just 5

Just 3 >> Nothing

操作Just 3的值为3。它在\_ -> n部分被忽略。总体结果为Nothing

Nothing >> Just 3

行动Nothing根本不会产生任何价值。那么它传递给>>=(或>>)的右操作数是什么?它没有!构建>>= monad的Maybe,如果左侧操作为Nothing,则根本不执行正确的操作,整体结果为Nothing

答案 2 :(得分:7)

要完成Sibi答案>>可以被视为其他语言中的;,如C或C ++ ..

当您使用C语言(或其他语言的同等语言)时

的printf("富&#34);    的printf("杆&#34);

你显然打印foobar(副作用),但对printf的调用也有一个返回值,在我们的例子中是打印的长度,即3 3.你有没有想过会发生什么这些数字? 他们被抛弃了,因为在C中, expr 1; exp 2 ,表示

  • 评估 expr1
  • 抛弃其结果
  • 评估 expr2

(那时,你可以问你为什么编译器为了放弃它的结果而无法评估 expr1 ?由于副作用。在{{1的情况下副作用是打印一些东西。你很少对返回的值本身感兴趣。)

所以printf可以看作是一个运算符,它采用2表达式并返回一个新表达式。这与;运算符完全相同。

写作时

>>

它完全等同于 print "foo" >> print "bar" ,除了(并且主要区别)printf("foo");printf("bar")不像C. >>中的;那样神奇。>>它是用户定义的运算符,可以为每种类型的Monad重新定义。 这就是为什么Haskell程序员非常喜欢Monad的原因:简而言之,它允许你重新定义自己;的行为。

正如我们所见,在C ;中只评估一个表达式并丢弃它的值。事实上,它有点复杂,因为它不是breakreturn。 Maybe monad中的Nothing可以被视为breakreturn>>评估第一个表达式,如果它是Nothing则停止。否则它会丢弃它的值并继续。

您可以在C中看到您的第一个示例(我认为它是有效的C)

3; return

return; 3

第一个例子,计算3,丢弃其值并返回。 第二个,直接回来。

回答问题when is it usefull?几乎所有的时候你使用IO,即使你很少看到它。

而不是写

 print "foo" >> print "bar"

Haskell提供了一种语法糖,它通过do-notation将(几乎)新行转换为>>, 所以你要写

do
  print "foo"
  print "bar"

严格等同于前一个版本(事实上,编号符号版本由编译器转换为前一版本。)

它甚至也相当于(即使很少使用)

do print "foo"; print "bar"

总结一下,>>可以看作是;的等价物,或换行符是其他语言,区别在于它的确切含义取决于上下文 (莫纳德正在采取行动)。 Maybe monad中的>>与IO Monad中的>>不同。

答案 3 :(得分:3)

首先让我们清楚你对Maybe monad的困惑。请考虑以下

instance Monad Maybe where
  return = Just
  (Just x) >>= g = g x
  Nothing  >>= _ = Nothing

正如您所看到的,Nothing >>= _Nothing。由于>>只是>>=的特殊情况,其中参数被忽略,结果是相同的。

这是因为Maybe通常用于表示可能失败的计算。这是告诉我们“一旦你失败,你总是失败”。

现在回答你的问题。

  1. 你提到的报价已经回答了。
  2. 在某些不需要操作结果的情况下,它很有用。例如,putStrLn的结果是(),这既不重要也不会有任何用处。

答案 4 :(得分:0)

原因很简单:单词实现需要两个操作:

  • >>=
  • >>

第一个执行操作并将其操作结果传递给另一个操作。例如:

Prelude> :t getLine
getLine :: IO String
Prelude> :t putStrLn
putStrLn :: String -> IO ()

两个函数:first getLine只返回用IO包装的String,它从stdin读取字符串并将其包装到IO。第二个putStrLn获取String并打印出来。绑定有以下类型:

:t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

它可以表示为IO String -> (String -> IO String) -> IO String,因此您可以通过执行getLine将这两个或多个函数与(>> =)合并,并将结果String传递给{{ 1}}:

putStrLn

因此,您可以在一个操作中组合这两个或更多功能。

第二个getLine >>= putStrLn 产生的几乎相同,但不需要先前的操作。

>>

它只是执行,第一个动作,而不是第二个动作,第一个动作的结果不需要第二个动作:

:t (>>)
(>>) :: Monad m => m a -> m b -> m b