Reygoch
Reygoch

Reputation: 1284

General function for reading numbers in Haskell

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

Answers (1)

leftaroundabout
leftaroundabout

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 Double1. 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

Related Questions