Haskell IO示例

4

我在他们的维基上阅读了关于Haskell中IO单子更深层次的工作原理,并遇到了这段代码

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

对我来说这很有道理。ask函数应该打印出给定的字符串并返回可传递到a或b的行。

然而,将其加载到GHCi中时,我遇到了问题。告诉我在使用ask时没有Read的实例,并且我可以导入GHC.Read。那是不必要的。这段代码在Haskell.org上,所以我认为它应该能够工作。是语言中有些东西被更改了还是我缺少某个重要的理解点?

2个回答

7
如果您只创建一个包含ask函数(没有问题的main函数)的文件,并将其加载到ghci中,您将能够看到ask函数的类型。
ask :: (Read a) => String -> IO a

这句话的意思是,它在返回类型上是多态的。

问题在于当你执行以下操作时:

a <- ask "What is your name"

编译器需要知道a的类型,以便使用正确的反序列化函数读取输入行。但是,a没有在任何其他地方使用,并且也没有类型签名,因此类型推断无法推断出a的类型。编译器放弃并给出“模棱两可的类型”消息。
有两种主要方法可以解决这个问题:
  1. Make the ask function always return the same type. You can do this either by adding an specific signature

    ask :: String -> IO String
    

    Or by changing the readLn into something like getLine.

  2. Add type signatures where you are using the polymorphic functions. You can either add a type signature to the ask call itself:

    a <- ask "What is your name" :: IO String
    

    or you can add it directly to the variable

    (a :: String) <- ask "What is your name"
    

    however, this second option isn't allowed by the default Haskell syntax. you need to enable the ScopedTypeVariables extention by adding the following comment as the first line in your file

    {-# LANGUAGE ScopedTypeVariables #-}
    

4

我已经对代码进行了两个更改。

  1. 修复缩进 - 请记住haskell是“空间敏感的”,所以请确保代码看起来对齐
  2. 明确类型签名。 这有点棘手。但是作为经验法则,当您预期工作的代码不起作用时。 尝试使用类型注释注释您的代码,如下所示。 您将在时间内理解为什么这样。

以下是修改后的代码:

main = do
  a <- askString "What is your name?"
  b <- askOther "How old are you?"

  putStrLn ""
  putStrLn "Name and age"
  putStrLn (a :: String)
  print (b :: Int)
  return ()

askString s = do
  putStrLn s
  getLine

askOther s = do
  putStrLn s
  readLn

编辑:抱歉,代码现在可以编译了。随着您的Haskell技能的提高,您将看到为什么askString和askOther看起来不同。这里是一个示例运行:

$ runghc Hello.hs
What is your name?
Arash
How old are you?
22

Name and age
Arash
22

askOther和askString之间的区别是否与它们作为一个传递Int,另一个传递String有关? - Ryan Cori
1
@RyanCori,askString :: String -> IO String,但是 askOther :: (Read a) => String -> IO a(请参考missingnos的优秀答案,了解如何将a转换为Int)。您也可以使用askOther来获取字符串,但是程序用户必须输入"Arash"而不是简单的Arash - Tarrasch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接