6

假设我编写了以下惊人的代码:

func = do
  a <- Just 5
  return a

这很没有意义,我知道。在这里,a5,并func返回Just 5

现在我重写了我很棒的(但毫无意义的)函数:

func' = do
  a <- Nothing
  return a

这个函数返回Nothing,但到底是a什么?没有什么可以从Nothing值中提取的,但是当我做这样的事情时,程序不会发牢骚:

func'' = do
  a <- Nothing
  b <- Just 5
  return $ a+b

我只是很难看到实际发生的事情。是什么a?换句话说:<- 实际上做了什么?说它“从右侧提取值并将其绑定到左侧”显然过于简单化了。我没有得到什么?

谢谢 :)

4

5 回答 5

11

让我们尝试对最后一个示例的 do-notation 进行脱糖。

func'' = Nothing >>= (\a -> Just 5 >>= (\b -> return $ a+b))

现在,让我们看看 >>= 是如何为 Maybe 定义的。它在前奏曲中:

instance  Monad Maybe  where
    (Just x) >>= k   =  k x
    Nothing  >>= k   =  Nothing
    return           =  Just
    fail s           =  Nothing

Nothing >>= foo简单Nothing

于 2012-04-18T07:49:06.460 回答
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只是线程化的结果。

于 2012-04-18T07:55:23.880 回答
5

让我们看一下Maybemonad的定义。

instance Monad Maybe where
  return = Just

  Just a  >>= f = f a
  Nothing >>= _ = Nothing

do在您的函数中对 -notation 进行脱糖:

func' =
  Nothing >>= \a ->
  return a

>>=is的第一个参数Nothing,从上面的定义我们可以看到它>>=忽略了第二个参数。所以我们得到:

func' = Nothing

由于该函数\a -> ...永远不会被调用,因此a永远不会被分配。所以答案是:a甚至没有达到。


至于去糖do符号,这里是它如何完成的一个快速草图(我做了一个简化 - 处理fail,即不匹配的模式):

do {a; rest} → a >> do rest

请注意,>>通常以>>=as的形式实现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
于 2012-04-18T07:50:32.713 回答
2

Nothing并不是真正的“无”,它实际上是Maybemonad 中某些东西的可能值:

data Maybe t = Nothing | Just t

也就是说,如果你有某种类型Maybe t的东西t,它可以有值Just xx类型的东西在哪里t Nothing; 从这个意义上说Maybe,只是扩展t为具有更多可能的值,Nothing. (它有其他属性,因为它是一个monad,但这并不真正关心我们,除了doand的语法糖<-。)

于 2012-04-18T07:57:06.923 回答
1

让我们举个例子:

func = do
  a <- Just 5
  return a

就像在其他编程语言中一样,您可以将其分为两部分,分别对应“到目前为止已经完成的工作”和“尚未完成的工作”。例如,我们可以在Just 5and

a <- ...
return a

在许多流行的编程语言中,您希望Just 5将 填充到变量a中并且代码将继续。

Haskell 做了一些不同的事情。“其余代码”可以被认为是一个函数,它描述了a如果你有一个值可以做什么。然后将此函数应用于Just 5. 但它不是直接应用的。它使用 apply 的任何定义来>>=应用,具体取决于表达式的类型。对于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并且在这种情况下>>=被定义为忽略“其余代码”而只是 return Nothing。这就是你得到的结果。

于 2012-04-19T01:51:52.457 回答