Let子句嵌套在嵌套在do子句中的if子句中

时间:2015-10-31 22:09:21

标签: haskell

我目前正在努力学习一下Haskell for Great Good,我正在尝试修改第九章“输入和输出”中的一个代码片段来正确处理错误:

main = do
    (command:args) <- getArgs
    let result = lookup command dispatch
    if result == Nothing
        then
            errorExit 
        else
            let (Just action) = result
    action args

,其中

dispatch :: [(String, [String] -> IO ())]

是一个关联列表

errorExit :: IO ()

是打印错误消息的函数。

使用GHC进行编译会显示错误消息

todo.hs:20:13: parse error in let binding: missing required 'in'

(据我的理解),似乎在说“让”这里并没有意识到它是在“做”块中。

在第5行和第7行添加“do”(分别在“then”和“else”之后),将错误消息更改为

todo.hs:20:13:
The last statement in a 'do' block must be an expression
  let (Just action) = result

todo.hs:21:5: Not in scope: `action'.

现在,虽然我同意第一条错误消息,但我还有一个变量已超出范围?我已经仔细检查了我的对齐方式,似乎没有任何不合适的地方。

在do块中的if子句中分配变量的适当方法是什么?

3 个答案:

答案 0 :(得分:3)

我的建议是首先不使用n=0 for i in /dev/sd[^a]*; do ((n++)) if /sbin/pvs | grep -q "$i"; then echo "/dev/$i configured" else echo "$n $i" fi done echo "\$n after loop: $n" ,使用if。通过使用case,您可以测试该值并将结果一次性绑定到变量。像这样:

case

要更深入地讨论为什么我们应该main = do (command:args) <- getArgs case lookup command dispatch of Nothing -> errorExit Just action -> action args 优先于case,请参阅boolean blindness

答案 1 :(得分:1)

您收到错误是因为您正在尝试比较函数类型的值。当您执行检查if result == Nothing时,它会尝试检查Nothing与[{1}}类型result的相等性。

因此,如果您希望它正确地进行类型检查,则必须为Maybe ([String] -> IO ())定义Eq个实例,并且因为您尝试比较两个函数是否相等而没有任何意义。

您也可以使用->编写代码:

fmap

答案 2 :(得分:1)

@svenningsson建议正确的解决方案。你的原文失败的原因是因为let 子句只能出现在do块的顶层 - 它们是简单的语法糖,不会查看内部表达式:

do let x = 1
   y

desugars到let 表达式

let x = 1 in y

唉,在do块中,像if ... then ... else ...这样的表达式子句无法在do块的其余部分中声明变量。

至少有两种方法可以解决这个问题。

  1. do的其余部分吸收到表达式中:

    main = do
        (command:args) <- getArgs
        let result = lookup command dispatch
        if result == Nothing
            then
                errorExit 
            else do
                let (Just action) = result
                action args
    

    (这实际上是@svenningsson在其更好的case版本中使用的方法。)

    如果需要将do表达式的其余部分复制到多个分支中,这可能会有点尴尬。

    (“秘密”技巧:GHC(与标准Haskell不同)实际上并不需要最终的内部do块比外部块缩进更多,这可以帮助缩进量开始变得烦人。)

  2. 将变量声明拉出表达式:

    main = do
        (command:args) <- getArgs
        let result = lookup command dispatch
        action <- if result == Nothing
            then
                errorExit 
            else do
                let (Just action') = result
                return action'
        action args
    

    这需要组成一个新的变量名,因为let子句中的模式不仅仅是一个简单的变量。

  3. 最后,action 总是在代码的最后一行超出范围,但GHC在几个阶段工作,如果它在解析阶段中止,则不会检查范围错误。 (出于某种原因,它会在稍后的阶段进行The last statement in a 'do' block must be an expression检查而不是解析。)

    附录:在我理解了@Sibi的含义之后,我发现result == Nothing无效,因此即使使用上述解决方法,也无法使用if ... then ... else ...