Haskell IO的例子

Rya*_*ori 4 io monads haskell

我正在阅读他们的wiki上Haskell中IO monad的更深层次的工作, 我遇到了这段代码

main = do a <- ask "What is your name?"
      b <- ask "How old are you?"
      return ()
ask s = do putStr s
       readLn
Run Code Online (Sandbox Code Playgroud)

这对我来说很有意义.ask函数应该打印出给它的字符串并返回一行可以传递给a或b.

然而,加载到GHCi我遇到问题.告诉我没有使用read的读取实例,我可以导入GHC.Read.这不应该是必要的.这段代码在Haskell.org上,所以我认为它应该有效.语言中的某些东西是否发生了变化,或者是否存在一些我缺失的重要理解?

hug*_*omg 7

如果你创建一个只有ask函数的文件(没有有问题的main)并将其加载到ghci,你将能够看到ask的类型是

ask :: (Read a) => String -> IO a
Run Code Online (Sandbox Code Playgroud)

意味着它在返回类型中是多态的.

问题是,当你这样做

a <- ask "What is your name"
Run Code Online (Sandbox Code Playgroud)

编译器需要知道什么是类型,a因此它可以使用正确的反序列化函数为您从输入读取的行.但是a在其他任何地方都没有使用,也没有任何类型的签名,所以类型推断不能推断出类型a.编译器放弃并为您提供"ambiguos类型"消息.

有两种主要方法可以解决这个问题:

  1. 使ask函数始终返回相同的类型.您可以通过添加特定签名来执行此操作

    ask :: String -> IO String
    
    Run Code Online (Sandbox Code Playgroud)

    或者通过改变readLn成类似的东西getLine.

  2. 在使用多态函数的位置添加类型签名.您可以在问题调用本身中添加类型签名:

    a <- ask "What is your name" :: IO String
    
    Run Code Online (Sandbox Code Playgroud)

    或者您可以将其直接添加到变量中

    (a :: String) <- ask "What is your name"
    
    Run Code Online (Sandbox Code Playgroud)

    但是,默认的Haskell语法不允许使用第二个选项.您需要通过添加以下注释作为文件的第一行来启用ScopedTypeVariables扩展

    {-# LANGUAGE ScopedTypeVariables #-}
    
    Run Code Online (Sandbox Code Playgroud)