Search code examples
haskellparsec

How to access Parsecs Input Stream directly


I want to access parsecs input stream directly which can be done using getParserState. To read from the stream the uncons method is provided. However I'm facing (as usual) a type related problem. so this is my parsing function:

myParser :: (Stream s m Char) => ParsecT s (ShiftedState u s) m String
myParser = do
  s <- P.getParserState
  let i = stateInput s
  let x = uncons i
  return ""

and I'm getting the following error.

Could not deduce (Stream s m0 Char)
        arising from a use of ‘uncons’

And the problem is simply that I do not quite know what exactly the error means. I think uncons runs in the underlying monad, not in the ParsecT monad. But I don't know how to lift (?) it.

Basically I would like to know how I can use uncons and read from the stream. Now please don't worry about wether one should do this... this is basically me understanding how monads work xD


Solution

  • Here’s what’s happening: uncons needs to run in some sort of monad. You can see this by its type signature: uncons :: Stream s m t => s -> m (Maybe (t, s)). But you’re just doing let x = uncons i — so x is a monadic computation which returns the result of uncons i, but you haven’t specified which monad this monadic computation is running within. You happen to know that you ‘want’ uncons i to run within your monad m (which satisfies Stream s m Char), but GHC doesn’t know this. So GHC makes the assumption that here, uncons i :: m0 (Maybe (t, s)), where m0 is some arbitrary monad. Thus GHC produces the error you see, since uncons requires the constraint Stream s m0 Char to be satisfied in order to use it, yet that constraint isn’t necessarily satisfied for any random m0.

    How can this be solved? Well, you already know that this whole computation is of type ParsecT s (ShiftedState u s) m Char, and m satisfies the instance Stream s m Char. And as it turns out, there is a function lift :: Monad m => m a -> ParsecT s u m a which ‘lifts’ a monadic computation m a to a Parsec computation ParsecT s u m a. So you can just rewrite your function to:

    myParser :: (Stream s m Char) => ParsecT s (ShiftedState u s) m String
    myParser = do
      s <- P.getParserState
      let i = stateInput s
      x <- lift (uncons i)
      return ""
    

    I haven’t tested it, but this should work if I haven’t made any mistakes.

    (Another solution is to simply give x a type signature:

    let x :: m (Maybe (Char, s))
        x = uncons i
    

    This forces x to have the type you want. Now GHC can look and see that m and s all satisfy the relevant constraint, so does not produce that error. But this requires the ScopedTypeVariables language extension to compile, and is much less elegant than the previous solution.)