我是Haskell的新手,我正在编写一个从JSON到其他格式的编译器。我不明白将表达式绑定到变量与传递内联之间的区别,这里有两个示例(在do
块内)我期望它们工作相同,但是第二个示例混淆了typechecker并且失败了:
按预期工作
modul <- parseModule <$> readFile "foo.json"
case (modul) of
(Left s) -> error s
(Right m) -> what (m :: Module) `shouldBe` "module"
未能进行类型检查
case (parseModule <$> readFile "foo.json") of
(Left s) -> error s
(Right m) -> what (m :: Module) `shouldBe` "module"
错误:
• Couldn't match type ‘Either a0’ with ‘IO’
Expected type: IO (Either String Module)
Actual type: Either a0 (Either String Module)
答案 0 :(得分:4)
parseModule <$> readFile "foo.json"
不是您可以模式匹配的值。该值不是纯粹的功能,因为它取决于现实世界的资源(文件)。
相反,它是一个 IO动作,它能够在执行时获取值(然后它会获取“世界状态的快照,冻结成纯值”)。
实际上,只有一种方法可以执行IO操作:将它们分配给名称main
,编译并运行。这可能听起来很疯狂 - 你只能在一个程序中有一个单一动作? - 但实际上并非如此,因为您可以将多个动作(按顺序执行)合并为一个动作并执行该动作。这个组成的动作是通过 monads 的概念完成的。在您的情况下,您基本上希望使用类型为IO (Either String Module)
的函数来编写Either String Module -> IO ()
类型的操作。这就是>>=
operator的用途。
main :: IO ()
main = parseModule <$> readFile "foo.json" >>= f
where f modul = case modul of ...
......或者是lambda风格,
main = parseModule <$> readFile "foo.json"
>>= \modul -> case modul of
Left s -> error s
Right m -> what m `shouldBe` "module"
因为经常需要这种绑定,所以Haskell有一个特殊的do
语法,你已经在第一个片段中使用了它。它只是这个>>=
运算符链的语法糖,但这种monadic绑定是必不可少的。