Harry M
Harry M

Reputation: 3

Read Strings from command line util one can be 'read' as a Float

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

Answers (2)

ErikR
ErikR

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

chi
chi

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

Related Questions