haskellmonadsmonad-transformershaskeline

readInputLine with IO String


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

Solution

  • 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