Search code examples
parsinghaskellparsec

Parsec simple parser results in error "Non type-variable argument in the constraint: Stream s m Char"


Suppose I just want to create my own parser which is exactly the same with the char in Parsec, but when I run

import Text.Parsec
char1 c = char c

It gives me

? Non type-variable argument in the constraint: Stream s m Char
  (Use FlexibleContexts to permit this)
? When checking the inferred type
    char1 :: forall s (m :: * -> *) u.
             Stream s m Char =>
             Char -> ParsecT s u m Cha

How can I solve it? Is there any other imports that I should include? Thanks


Solution

  • Parsec parsers have rather complicated types due to their flexibility. You can get around this in two ways:

    1. Put {-# Language FlexibleContexts #-} at the top of your source file. This pragma tells GHC to do some extra type inference.

    2. (Recommended) Give char1 an explicit type.

      char1 :: Char -> Parsec String () Char   
      char1 c = char c
      

    This is recommended because you should always give any top-level declaration an explicit type. If you don't then all the compiler can tell you is that there is a type mismatch somewhere in your code (it will tell you where it found the contradiction, but that's not likely to be where the error is). With explicit type declarations the compiler can narrow it down.

    In this case, Parsec defines the Parsec type as

    type Parsec s u = ParsecT s u Identity
    

    You will recognise ParsecT from your error message. ParsecT is a monad transformer: it lets you have the parser do things in other monads (like IO) when it recognises the text. In this case we only want a parser, so we use the Identity monad, which does nothing.

    The s parameter is the stream of inputs. In this case we will say that the parser is taking its input from a String.

    The u parameter is a state. This allows you to do stuff like recording variable names in a symbol table as you encounter them. In this case we don't use a state so its just ().

    If you were writing a real parser then you would generally define your own type synonym, something like

    type FooParser a = Parsec Text FooState a
    
    char1 :: Char -> FooParser Char
    char1 = char