所以,我想从用户那里获得Float
并且我已经完成了这个功能:
getFloat :: IO Float
getFloat = do
string <- getLine
return (read string :: Float)
现在我想知道如何制作一个更通用的功能,可以一次性返回整数,双精度和浮点数。
我尝试使用Num
类型类来容纳更多可能性,但它不起作用。
这是我所拥有的,它编译但是我不确定我在这里做了什么,也不确定它是否有效。
etNumber :: (Read a) => IO a
getNumber = do
string <- getLine
return (read string)
答案 0 :(得分:2)
撰写签名时
getNumber' :: Num a => IO a
这意味着此操作需要能够提供调用者可能请求的任何结果类型a
- 前提是该类型是Num
类的实例。基本上,该操作对它必须生成的类型一无所知,尽管它可以使用该特定Num
实例的方法:
class Num a where
fromInteger :: Integer -> a
(+) :: a -> a -> a
...
请注意,这并没有为您提供任何生成小数/浮点数的工具。数字,只有整数。你其实可以写
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
在这种情况下,输入首先被读取为任意精度的有理类型(可以处理小数分数输入),然后转换为所需的最终类型,如Double
1 < / SUP>。但是,这可能不是一个整数类型,因为那些显然无法处理可能的小数输入!
也许你想象的是这个:
getNumber''' :: IO (∃a. Num a => a)
这将是存在类型。这基本上是一个受约束的动态类型,即类型不是通过推断来自调用者所需的编译类型而选择的,而是在运行时选择一个能够正确处理特定字符串输入的合适类型(如果可能的话,整数,如果需要,浮动)。
好吧,Haskell没有 2 存在类型和for good reasons。它的标准参数多态更有用,因为您实际上得到的保证是某些类型将与您在编译时指定的完全相同。价值应该是积分?设为Integer
;如果然后在输入中出现小数分数,则会获得有意义的错误消息。价值可能是分数?设为Rational
或Double
;这些当然也包括整数值。
无论如何,没有任何理由将行动限制在任何数字类别。如果你把它变成多态的话,你应该只根据实现的需要来约束它(即Read
)。总结一下:只需使用readLn
,就不要写任何getTʏᴘᴇ
动作。
1 作为一般规则,never use Float
除非您确定Double
不是您想要的。
2 嗯,它有一个(通常被推荐的)解决方法:存在限定的记录构造函数。
{-# LANGUAGE GADTs #-}
data SomeNum where
SomeNum :: Num a => a -> SomeNum
getNumber'''' :: IO SomeNum