0

I would like to read an input string with a custom prompt, however the prompt string comes from impure context, thus I can't use readInputLine as is. I've tried to implement a function based on this answer

getLineIO :: MonadException m => IO String -> InputT m (Maybe String)
getLineIO ios = do
    s <- ios
    res <- getInputLine s
    lift res

but I get an error

   Couldn't match expected type ‘InputT m String’
                with actual type ‘IO String’
    Relevant bindings include
      getLineIO :: IO String -> InputT m (Maybe String)
        (bound at Main.hs:38:1)
    In a stmt of a 'do' block: s <- ios
    In the expression:
      do { s <- ios;
           return $ getInputLine s }

Update: got it to work based on @bheklilr's answer

getLineIO :: (MonadException m, MonadIO m) => IO String -> InputT m (Maybe String)
getLineIO ios = do
      s <- liftIO ios
      getInputLine s
Community
  • 1
  • 1
dimid
  • 7,285
  • 1
  • 46
  • 85
  • 1
    You probably just need to use `lift` to convert your `IO String` into an `InputT IO String` i.e. `s <- lift ios`. – Lee Jun 20 '16 at 15:24

1 Answers1

3

The code

do
    s <- ios
    res <- getInputLine s
    lift res

Gets de-sugared into

ios >>= \s -> (getInputLine s >>= \res -> lift res)

Where

(>>=) :: Monad m => m a -> (a -> m b) -> m b

This type signature means that m has to be the same Monad instance throughout. You've given it ios :: IO String and \s -> getInputLine s :: String -> InputT n (Maybe String), but m can't be both IO and InputT n, hence the compiler error.

You can just use liftIO on ios provided instance MonadIO m => MonadIO (InputT m) is defined, which it is. So you can just do

getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String)
getLineIO ios = do
    s <- liftIO ios
    res <- getInputLine s
    lift res
bheklilr
  • 53,530
  • 6
  • 107
  • 163
  • Thanks, are you using `m` for both `MonadException` and `MonadIO`? – dimid Jun 20 '16 at 15:32
  • 1
    @dimid `liftIO` requires that the `Monad` you're in also implements `MonadIO`. After looking at the [documentation](https://hackage.haskell.org/package/haskeline-0.7.2.3/docs/System-Console-Haskeline-MonadException.html) it appears that `MonadException` already requires `MonadIO`, so you can drop that requirement. However, if you were working in a different `Monad` it is possible to have multiple constraints on your particular `m`. It's not uncommon to see `mtl` based code with constraints like `(MonadState s m, MonadWriter w m, MonadIO m) => ...` – bheklilr Jun 20 '16 at 16:30