我目前正在努力学习一下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子句中分配变量的适当方法是什么?
答案 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)
let
子句只能出现在do
块的顶层 - 它们是简单的语法糖,不会查看内部表达式:
do let x = 1
y
desugars到let
表达式
let x = 1 in y
唉,在do
块中,像if ... then ... else ...
这样的表达式子句无法在do
块的其余部分中声明变量。
至少有两种方法可以解决这个问题。
将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
块比外部块缩进更多,这可以帮助缩进量开始变得烦人。)
将变量声明拉出表达式:
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
子句中的模式不仅仅是一个简单的变量。
最后,action
总是在代码的最后一行超出范围,但GHC在几个阶段工作,如果它在解析阶段中止,则不会检查范围错误。 (出于某种原因,它会在稍后的阶段进行The last statement in a 'do' block must be an expression
检查而不是解析。)
附录:在我理解了@Sibi的含义之后,我发现result == Nothing
无效,因此即使使用上述解决方法,也无法使用if ... then ... else ...
。