Haskell绑定到变量Vs内联声明

时间:2018-02-19 22:47:34

标签: haskell

我是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)

1 个答案:

答案 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绑定是必不可少的。