2

Honestly, I feel like this must have a dupe somewhere, but I couldn't find it even after searching .

Say I have the following code to simply get read a double from the user and echo it back:

import qualified Control.Monad.Except as E
import Text.Read(readMaybe) 

data Error = ParseError String
             | Default String deriving (Show)

type ThrowsError = Either Error 

main = do
  putStrLn "Enter your number: "
  val <- getDouble
  print val

parseString :: String -> ThrowsError Double
parseString val = maybe (E.throwError $ ParseError val) return 
                        (readMaybe val :: Maybe Double)

getDouble :: ThrowsError Double
getDouble = getLine >>= parseString

This breaks in two places:

  1. In main, putStrLn is type IO Double but getDouble is type ThrowsError Double.

  2. In getDouble, getLine is type IO Double but parseString returns IO Double.

Essentially, I'd want to be able to extract the value out of the IO monad, apply computations on it, and put it back inside the appropriate monad. However, the bind function seems to expect the same monad types for input and output, so what I want to do doesn't work.

What's the way around it?

gust
  • 878
  • 9
  • 23
  • 3
    The thing is, you can't just unwrap an arbitrary monad. So what you do instead is stack monads together with monad transformers. You need something like `type ThrowsError m = ExceptT Error m` and `getDouble :: ThrowsError IO Double`, and put `lift` in appropriate places – MorJ Jul 02 '20 at 04:27
  • It's always the other way around: With monad you cannot freely extract values. But you can lift your operation into the context of the monad. Lifting is the functor operation. –  Jul 02 '20 at 14:51

1 Answers1

9

You don't need any transformers. parseString is a pure function, so to apply it to a monadic action you use fmap (aka (<$>)), not (>>=) as you have.

getDouble :: IO (ThrowsError Double)
getDouble = parseString <$> getLine

You would use (>>=) if parseString returned an IO something.

luqui
  • 59,485
  • 12
  • 145
  • 204