读取 Haskell 中数字的通用函数

General function for reading numbers in Haskell

所以,我想从用户那里得到一个 Float,我已经实现了这个功能:

getFloat :: IO Float
getFloat = do
    string <- getLine
    return (read string :: Float)

现在我想知道如何制作一个更通用的函数,可以 return 整数、双精度和浮点数。

我尝试使用 Num 类型 class 来容纳更多的可能性,但它不起作用。

据我所知,它可以编译,但我不确定我在这里到底在做什么,也不确定它是否有效。

etNumber :: (Read a) => IO a
getNumber = do
    string <- getLine
    return (read string)

写签名的时候

getNumber' :: Num a => IO a

这意味着此操作将需要能够提供调用者可能请求的任何结果类型 a – 前提是该类型是 Num class 的实例。所以本质上,动作对它必须产生的类型一无所知,尽管它可以使用特定 Num 实例的方法:

class Num a where
  fromInteger :: Integer -> a
  (+) :: a -> a -> a
  ...

请注意,这不会为您提供任何生成 fractional/floating-pt 的工具。数字,只有整数。你实际上可以写

getNumber' :: Num a => IO a
getNumber' = do
   i <- readLn
   return $ fromInteger i

但这非常无用,如果您实际上尝试将 0.3 之类的内容读取为浮点数,它确实会失败——因为无法通过中间 Integer 类型将其拉出。

你可以这样做:

getNumber'' :: Fractional a => IO a
getNumber'' = do
   q <- readLn
   return $ fromRational q

在这种情况下,输入将首先被读取为任意精度的有理数类型(可以处理小数输入),然后转换为所需的最终类型,如Double1。但是,这不能不是整数类型,因为那些显然无法处理可能的小数输入!

也许你的设想是这样的:

getNumber''' :: IO (∃a. Num a => a)

这将是一个 存在类型 。这基本上是一个受约束的 动态类型 ,即类型不是通过从调用者想要的推断在编译类型中选择的,而是在运行时选择可以正确处理的合适类型特定的字符串输入(尽可能为整数,如果需要则为浮动)。

嗯,Haskell 没有2 存在类型,for good reasons。它的标准参数多态性更有用,因为您实际上可以保证某些类型正是您在编译时指定的类型。值应该是整数?使它成为 Integer;如果在输入中出现小数部分,您就会正确地收到一条有意义的错误消息。价值可能是小数?设为 RationalDouble;这些当然也包括整数值。

无论如何,没有真正的理由将操作限制为任何数字 class。如果你完全让它成为多态的,你应该只在实现所需的范围内限制它(即 Read)。总结一下:直接用readLn,根本不写任何getTʏᴘᴇ动作


1作为一般规则,never use Float 除非您确定 Double 不是您想要的。

2好吧,它有一个(通常不推荐的)解决方法:存在限定的记录构造函数。

{-# LANGUAGE GADTs #-}
data SomeNum where
  SomeNum :: Num a => a -> SomeNum
getNumber'''' :: IO SomeNum