Reputation: 1284
So, I wanted to get a Float
from user and I have made this function :
getFloat :: IO Float
getFloat = do
string <- getLine
return (read string :: Float)
Now I would like to know how to make a more general function that can return ints, doubles and floats all in one go.
I have tried using Num
type class to accomodate more possibilities but it doesn't work.
This is as far as I have got, it compiles but I'm not sure what I'm doing here exactly nor if it works at all.
etNumber :: (Read a) => IO a
getNumber = do
string <- getLine
return (read string)
Upvotes: 1
Views: 644
Reputation: 120711
When you write the signature
getNumber' :: Num a => IO a
it means this action will need to be able to offer any result type a
that the caller might request – provided the type is an instance of the Num
class. So essentially, the action knows nothing about the type it has to produce, though it can use the methods of that particular Num
instance:
class Num a where
fromInteger :: Integer -> a
(+) :: a -> a -> a
...
Note that this does not give you any tools to generate fractional/floating-pt. numbers, only integer ones. You could in fact write
getNumber' :: Num a => IO a
getNumber' = do
i <- readLn
return $ fromInteger i
but this is pretty useless, indeed it'll fail if you actually attempt to read something like 0.3
to a float – because that can't be pulled through the intermediate Integer
type.
You could do this:
getNumber'' :: Fractional a => IO a
getNumber'' = do
q <- readLn
return $ fromRational q
in this case, the input will first be read as an arbitrary-precision rational type (which can deal with decimal-fraction input) and then converted to the desired final type, like Double
1. However, this can not be an integer type, because those obviously could not handle the possible fractional inputs!
Maybe what you envision is this:
getNumber''' :: IO (∃a. Num a => a)
which would be an existential type. That's basically a constrained dynamic type, i.e. the type is not chosen at compile-type by inference from what the caller wants, but instead at runtime, choosing a suitable type that can properly deal with the particular string input (integer if possible, floating if needed).
Well, Haskell doesn't have2 existential types, and for good reasons. Its standard parametric polymorphism is rather more useful because you actually get guarantees that some type will be exactly what you specify at compile time. Value should be integral? Make it an Integer
; if then a decimal-fraction turns up in the input you get a meaningful error message right a way. Value might be fractional? Make it Rational
or Double
; these of course include integral values as well.
At any rate, there's no real reason to constrain the action to any numerical class. If you make it polymorphic at all, you should constrain it only as much as needed for the implementation (i.e., Read
). To sum up: simply use readLn
, don't write any getTʏᴘᴇ
action at all.
1As a general rule, never use Float
except when you're sure Double
is not what you want.
2Well, it has a (generally disrecommended) workaround: existentially qualified record constructors.
{-# LANGUAGE GADTs #-}
data SomeNum where
SomeNum :: Num a => a -> SomeNum
getNumber'''' :: IO SomeNum
Upvotes: 2