这个 getRight :: Either a b -> Maybe b 怎么工作?

5

HaskellWiki的Do notation considered harmful中,Useful applications一节中,我发现:

It shall be mentioned that the do sometimes takes the burden from you to write boring things.

E.g. in

getRight :: Either a b -> Maybe b
getRight y =
   do Right x <- y
      return x

a case on y is included, which calls fail if y is not a Right (i.e. Left), and thus returns Nothing in this case.

我想尝试在模式不匹配时调用 fail (Nothing),这听起来很有趣。然而,语法看起来是错误的——我们不在Either单子中,那么我们怎么能从y中提取任何东西呢?

实际上,我尝试了一下,结果出现了 "Couldn't match type `Either a' with `Maybe'"。所以让我们在这里使用正确的模式匹配器,也就是let

getRight y = do { let (Right x) = y; return x }

这给了我一个语法错误 "解析错误,输入为`}'"。虽然我不理解为什么它不能工作,但让我们用多行注释来写出它:
getRight y = do
    let (Right x) = y
    return x

啊,看起来至少解析是有效的。但是:
*Main> getRight (Right 5)
Just 5
*Main> getRight (Left 5)
Just *** Exception: […]\test.hs:16:13-25: Irrefutable pattern failed for pattern (Data.Either.Right x)
-- `Nothing` was expected

发生了什么?所以我的问题是:

  • 这里发生了什么?为什么我的分号大括号行不起作用?
  • 如何正确做(使用do,其他都很简单)?

1
getRight 的解析错误是因为一般来说,一旦你切换到显式的大括号和分号,那么所有嵌套在内部的语言结构也必须使用显式的大括号和分号:getRight y = do { let { Right x = y }; return x } - kosmikus
@kosmikus:嗯,我得到了一个“在输入'='时解析错误”。 - Bergi
不知道。对我来说,它可以在ghc-7.4.2,ghc-7.6.3和ghc-7.8.1-rc1上工作... - kosmikus
1
@kosmikus:哦,不用在意。这是初学者犯的错误:我忘记了在ghci中的第一个let getRight x = …中加上“let”。 - Bergi
1个回答

9
这个例子可能是为了说明:
getRight :: Either a b -> Maybe b
getRight y =
   do Right x <- return y -- note: return = Just
      return x

当模式匹配失败时,会调用fail = const Nothing。它被翻译成:

getRight y = let ok (Right x) = do {return x}
                 ok _         = fail "pattern mismatch error"
             in return y >>= ok

就我个人而言,大多数经验丰富的专业人士认为将 fail 作为 Monad 方法是一个缺点。可以查看 MonadPlus ,了解一种更有原则的处理失败的方法。


2
那个页面通常会有一些问题...例如,整段文字已经过时,但脚注却说它们已经过时了(为什么不直接删除呢?)。 - Cubic
1
所以只有 <- 符号在模式不匹配时才会调用 fail,而 let 不会?你能展示一下编译器在去糖化 do 时生成的代码吗? - Bergi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接