说我写了以下amazin代码:
func = do
a <- Just 5
return a
我知道,这是毫无意义的。在此,a
为5
,func
返回Just 5
。
现在我改写了我很棒的(但没有意义的)功能:
func' = do
a <- Nothing
return a
这个函数返回Nothing
,但到底是什么a
?没有什么可以从Nothing
值中提取,但是当我做这样的事情时程序不会抱怨:
func'' = do
a <- Nothing
b <- Just 5
return $ a+b
我很难看到实际发生的事情。什么是a
?换句话说:<-
实际做什么?说它“从右侧提取值并将其绑定到左侧”显然过度简化了它。我没有得到什么?
谢谢:)
答案 0 :(得分:11)
让我们尝试去除最后一个例子的记号。
func'' = Nothing >>= (\a -> Just 5 >>= (\b -> return $ a+b))
现在,让我们看看如何为Maybe定义&gt;&gt; =。它出现在序曲中:
instance Monad Maybe where
(Just x) >>= k = k x
Nothing >>= k = Nothing
return = Just
fail s = Nothing
所以Nothing >>= foo
只是Nothing
答案 1 :(得分:6)
答案在于Monad
Maybe
实例的定义:
instance Monad Maybe where
(Just x) >>= k = k x
Nothing >>= _ = Nothing
(Just _) >> k = k
Nothing >> _ = Nothing
return = Just
您的func''
转换为:
Nothing >>= (\a -> (Just 5 >>= (\b -> return (a+b))))
从(>>=)
的定义中,您可以看到第一个Nothing
只是通过了结果。
答案 2 :(得分:5)
让我们看一下Maybe
monad的定义。
instance Monad Maybe where
return = Just
Just a >>= f = f a
Nothing >>= _ = Nothing
并且在你的函数中使用do
- 符号:
func' =
Nothing >>= \a ->
return a
>>=
的第一个参数是Nothing
,从上面的定义我们可以看出>>=
只是忽略了第二个参数。所以我们得到:
func' = Nothing
由于永远不会调用函数\a -> ...
,因此永远不会分配a
。所以答案是:a
甚至没有达到。
关于desugaring do
- 符号,这里有一个快速草图,说明它是如何完成的(我已经做了一个简化 - 处理fail
,即模式不匹配):
do {a; rest} → a >> do rest
请注意,>>
通常以>>=
为a >>= \_ -> do rest
实现(即第二个函数忽略参数)。
do {p <- a; rest} → a >>= \p -> do rest
do {let x = a; rest} → let x = a in do rest
最后:
do {a} = a
以下是一个例子:
main = do
name <- getLine
let msg = "Hello " ++ name
putStrLn msg
putStrLn "Good bye!"
去往:
main =
getLine >>= \name ->
let msg = "Hello " ++ name in
putStrLn msg >>
putStrLn "Good bye!"
为了让那些好奇的人完整,这里是do {p <- a; rest}
的“正确”翻译(直接取自Haskell报告):
do {pattern <- a; rest} → let ok pattern = do rest
ok _ = fail "error message"
in a >>= ok
答案 3 :(得分:2)
Nothing
并非真的“没有”,它实际上是Maybe
monad中某些内容的可能价值:
data Maybe t = Nothing | Just t
也就是说,如果某些类型Maybe t
的类型为t
,则其值Just x
(其中x
为{{1}类型}})或 t
;在这个意义上,Nothing
只是扩展Maybe
以获得另一个可能的值t
。 (它有其他属性,因为它是monad,但除了Nothing
和do
的句法糖之外,这并不是我们真正关心的问题。)
答案 4 :(得分:1)
我们举个例子:
func = do
a <- Just 5
return a
就像在其他编程语言中一样,你可以把它分成两部分,对应于“到目前为止做了什么”和“还有什么要做”。例如,我们可以在Just 5
和
a <- ...
return a
在许多流行的编程语言中,您希望将Just 5
填充到变量a
中,代码将继续。
Haskell做了不同的事情。 “代码的其余部分”可以被认为是一个函数,描述了如果你有一个值可以用a
做什么。然后将此函数应用于Just 5
。但它并没有直接应用。它适用于>>=
适用的任何定义,具体取决于表达式的类型。对于Maybe
,定义了>>=
,以便在处理Just X
时,“其余代码”功能应用于X
。但它也被定义为在处理Nothing
时,“代码的其余部分”功能被忽略,并返回Nothing
。
我们现在可以解释您的其他示例
func'' = do
a <- Nothing
b <- Just 5
return $ a+b
将其分为Nothing
和:
a <- ...
b <- Just 5
return $ a+b
正如我上面所说,这段代码可以被认为是应用于a
的可能值的函数。但它与Nothing
一起使用,在这种情况下,>>=
被定义为忽略“其余代码”并返回Nothing
。这就是你得到的结果。