Reputation: 3
I'm trying to write code that will prompt the user to enter a Float and will continue to do so until a valid float is entered.
I've tried the following approach:
getFloat :: Float
getFloat = do
input <- getLine
case (readMaybe input :: Maybe Float) of Just f -> f
Nothing -> do getFloat
But I'm getting the following error:
Main.hs:41:5:
Couldn't match type `IO b0' with `Float'
Expected type: IO String -> (String -> IO b0) -> Float
Actual type: IO String -> (String -> IO b0) -> IO b0
In a stmt of a 'do' block: input <- getLine
In the expression:
do { input <- getLine;
case (readMaybe input :: Maybe Float) of {
Just f -> f
Nothing -> do { ... } } }
In an equation for `getFloat':
getFloat
= do { input <- getLine;
case (readMaybe input :: Maybe Float) of {
Just f -> f
Nothing -> ... } }
Main.hs:42:56:
Couldn't match expected type `IO b0' with actual type `Float'
In the expression: f
In a case alternative: Just f -> f
Main.hs:43:60:
Couldn't match expected type `IO b0' with actual type `Float'
In a stmt of a 'do' block: getFloat
In the expression: do { getFloat }
I'm a beginner a would very much appreciate if someone could explain what am I missing here.
Upvotes: 0
Views: 111
Reputation: 52057
For the Just case, use -> return f
instead of -> f
.
And then just remove the type signature for getFloat
. After it compiles, have ghci tell you what the type signature for getFloat
is.
Complete code:
getFloat = do
input <- getLine
case (readMaybe input :: Maybe Float) of Just f -> return f
Nothing -> do getFloat
Update
You might be interested in this highly-polymorphic version of the loop:
{-# LANGUAGE NoMonomorphismRestriction #-}
import Text.Read
getItem = do
input <- getLine
case readMaybe input of
Nothing -> getItem
Just x -> return x
I have purposely written getItem
without a type signature - this is something that GHC can infer and fill in for you. I've also used the NoMonomorphismRestriction pragma so that getItem
remains polymorphic.
The idea is that getItem
can be used for any type that can be read - Floats, Doubles, Strings, etc. The type used by readMaybe
can be controlled by the caller in various ways. Here are some examples:
main = do
f1 <- getItem
f2 <- getItem
let r = f1 + f2 :: Float
print r
By forcing r
to be type Float, f1
and f2
must also be Floats, and therefore getItem
will try to parse a Float.
Here is another way to influence the type that readMaybe
uses:
main = do
f <- getItem :: IO Float
i <- getItem :: IO Int
print $ f^i -- compute f raised to the i-th power
Upvotes: 2
Reputation: 116174
getFloat :: Float
This states that getFloat
is a constant of type Float
, which is not what you want.
getFloat :: IO Float
This instead states that getFloat
is an IO action producing a Float
.
Once this is fixed, then you need to add return
in front of your f
, as @ErikR already explained. The return turns the pure float value f
into an IO action which produces it, without actually performing any IO.
Finally, you do not need the do
in the last do getFLoat
. The do
syntax is useful to sequence IO actions: if you only have one, it is redundant.
Upvotes: 1