Haskell 如何在IO中正确使用readMaybe函数

Haskell 如何在IO中正确使用readMaybe函数,haskell,Haskell,大约4个月前,我在Haskell开始编程,现在我必须处理Haskell的IO系统。 我已经做了很多IO操作,也没有遇到任何我自己无法解决的问题,但这次我在谷歌上搜索了将近两个小时,没有得到关于函数readMaybe的一些信息。因此,我有以下问题要解决,我已经尝试了许多不同的方法来解决它,但一直以来,我从我的编译器得到相同的失败消息: No instance for (Read a0) arising from a use of `readMaybe' The type variable `a0'

大约4个月前,我在Haskell开始编程,现在我必须处理Haskell的IO系统。 我已经做了很多IO操作,也没有遇到任何我自己无法解决的问题,但这次我在谷歌上搜索了将近两个小时,没有得到关于函数readMaybe的一些信息。因此,我有以下问题要解决,我已经尝试了许多不同的方法来解决它,但一直以来,我从我的编译器得到相同的失败消息:

No instance for (Read a0) arising from a use of `readMaybe'
The type variable `a0' is ambiguous
我明白编译器想告诉我什么,但我不知道如何解决这个问题。我已经尝试添加类约束,但没有成功。 这是我的一个非常小而简单的程序,它只计算用户输入的有效数字。当用户输入空行时,程序将终止。 这只是我想在以后的项目中使用的一个辅助功能

countNumbers :: IO Int
countNumbers = do
       x <- count 0
       return x where
          count :: Int -> IO Int
          count n = do
              line <- getLine
              case line of
                 "" -> do
                    return n
                 _  -> case readMaybe line of
                    Just _ -> do
                       x <- count (n+1)
                       return x
                    Nothing -> do
                       x <- count n
                       return x
对我来说非常奇怪的是,我已经编写了这样一个使用readMaybe函数的函数,它工作得非常好。。。 这个程序只是要求用户输入一个数字,只要用户输入一个有效的数字,它就会一直询问

getLineInt :: IO Int
getLineInt = do
      putStrLn "Please enter your guess"
      line <- getLine
      case readMaybe line of
            Just x -> do
               return x
            Nothing -> do
               putStrLn "Invalid number entered"
               x <- getLineInt
               return x
getLineInt::IO Int
getLineInt=do
putStrLn“请输入您的猜测”
行吗
返回x
无事可做
putStrLn“输入的号码无效”
为什么会发生这种情况?
在第二个函数中,很明显,
readMaybe line
用作
String->Maybe Int
,因为类型推断注意到您使用
返回x
,因此
x
必须是
Int

在第一个函数中,您根本不使用
的值,您只想检查
读取是否成功。但是,由于没有指定类型(类型推断既不显式也不隐式),因此类型变量不明确:

_  -> case readMaybe line of
有一个简单的解决方法:注释类型:

_  -> case readMaybe line :: Maybe Int of
顺便说一下,这与在没有任何类型上下文的情况下在
ghci
中使用
read
时遇到的行为完全相同:

> read "1234" <interactive>:10:1: No instance for (Read a0) arising from a use of `read' The type variable `a0' is ambiguous 现在如何计算数字?数字是那些单词,其中
readMaybeInt
不会返回
Nothing

countNumbers :: String -> Int
countNumbers = length . filter isJust . map readMaybeInt . words
现在如何计算标准输入中的数字?我们只需获取输入,直到一行完全为空,在所有这些行上映射
countNumbers
,然后映射
sum

lineNumberCount :: IO Int
lineNumberCount = 
  getContents >>= return . sum . map countNumbers . takeWhile (/= "") . lines
如果您不习惯bind方法,那么基本上

lineNumberCount :: IO Int
lineNumberCount = do
  input <- getContents
  return . sum . map countNumbers . takeWhile (/= "") . lines $ input
现在IO monad中只有一个函数,所有函数基本上都是标准函数的应用。请注意,
getContents
将关闭标准输入的句柄。如果你想使用,最好使用

input :: String -> IO [String]
input delim = do
  ln <- getLine 
  if ln == delim then return []
                 else input delim >>= return . (ln:)

这与IO无关,因此您可能不理解编译器试图告诉您的内容。
readMaybe
的签名中有一个类型变量
a
a
必须有一个
Read
实例,但除此之外,它可以是任何东西。编译器告诉您,它无法确定您想要的
a
是什么

getLineInt
中,您没有这个问题,因为您正在返回
readMaybe
的结果,并且类型签名说它应该是
Int
。在
countNumbers
中,您没有使用
readMaybe
的结果,因此没有任何东西可以用来确定正确的类型。您可以通过添加显式类型签名来解决此问题(我选择了
Int
,因为您显然在计算数字):

最后一句话是关于
do
notation的:它只是语法上的糖分,你不必一直使用它。您只需编写
returnx
,而不是
doreturnx

x <- getLineInt
return x
这让事情更具可读性:

getLineInt :: IO Int
getLineInt = do
  putStrLn "Please enter your guess"
  line <- getLine
  case readMaybe line of
    Just x -> return x
    Nothing -> putStrLn "Invalid number entered" >> getLineInt
getLineInt::IO Int
getLineInt=do
putStrLn“请输入您的猜测”
行返回x
Nothing->putStrLn“输入的数字无效”>>getLineInt

嘿,感谢您快速准确的回答,也感谢您对do符号用法的提示,我不知道这一点。我认为我必须在每个IO操作之前使用关键字do…不仅do从来都不是必需的,它实际上是一元绑定方法(>>=和>>)的语法糖,因此do可以用于任何一元,而不仅仅是IO。
import Control.Monad (liftM)
import Data.Maybe (isJust)
import Text.Read (readMaybe)

readMaybeInt :: String -> Maybe Int
readMaybeInt = readMaybe

countNumbers :: String -> Int
countNumbers = length . filter isJust . map readMaybeInt . words

lineNumberCount :: IO Int
lineNumberCount = 
  getContents >>= return . sum . map countNumbers . takeWhile (/= "") . lines
input :: String -> IO [String]
input delim = do
  ln <- getLine 
  if ln == delim then return []
                 else input delim >>= return . (ln:)
lineNumberCount :: IO Int
lineNumberCount = 
  input "" >>= return . sum . map countNumbers
_ -> case readMaybe line :: Maybe Int of
x <- getLineInt
return x
getLineInt
getLineInt :: IO Int
getLineInt = do
  putStrLn "Please enter your guess"
  line <- getLine
  case readMaybe line of
    Just x -> return x
    Nothing -> putStrLn "Invalid number entered" >> getLineInt