I am currently trying to study and understand the source code of the attoparsec
library, but there are some details I can't figure out myself. For example, the definition of the Parser
type:
newtype Parser i a = Parser {
runParser :: forall r.
State i -> Pos -> More
-> Failure i (State i) r
-> Success i (State i) a r
-> IResult i r
}
newtype Pos = Pos { fromPos :: Int }
deriving (Eq, Ord, Show, Num)
data IResult i r =
Fail i [String] String
| Partial (i -> IResult i r)
| Done i r
type Failure i t r = t -> Pos -> More -> [String] -> String
-> IResult i r
type Success i t a r = t -> Pos -> More -> a -> IResult i r
What I don't fully understand yet is the usage of the type-parameter r
. What would be different if I defined the type signature of runParser
like this:
State i -> Pos -> More -> Failure i (State i) a -> Success i (State i) a a -> IResult i a
?
Can you please help me to understand what forall r.
exactly means in this case and why it is necessary to use it in the runParser
's type signature?
Many thx in advance!
UPDATE: To clarify my question further: What I currently don't understand is why it is necessary to bring in the type-parameter r
in the first place. One could imagine, that the Parser
type could have been also defined like this:
newtype Parser i a = Parser {
runParser ::
State i -> Pos -> More
-> Failure i (State i) a
-> Success i (State i) a
-> IResult i a
}
data IResult i a =
Fail i [String] String
| Partial (i -> IResult i a)
| Done i a
type Failure i t a = t -> Pos -> More -> [String] -> String
-> IResult i a
type Success i t a = t -> Pos -> More -> a -> IResult i a
where the type-parameter r
is not used at all. And my question is why this definition would be "wrong" and what problems it would entail...
attoparsec creates continuation passing style (CPS) parsers and without the
forall
we wouldn't be able to chain parsers together.Here is a dramatically simplified version of the the types involved and definition of
bindP
- the monadic bind operator. We have eliminated the failure continuation and the input source.Notes that
Success a r
is simply the function typea -> r
.If we replace the definition of
runParser
with:we'll get a type error at the above underlined location. To understand this one can work out the following types:
but from the expression
(g a)
we can also conclude thata
has typea
which gives us our type error.Basically
Parser a
can be thought of as a method (or computation) of generating value of typea
andrunParser p ks
is the way to take that value and feed it to a function which takes ana
. The continuation functionks
can have typea -> r
for anyr
- the only requirement is that its input type isa
. By usingSuccess a a
in definingrunParser
we limit the applicability ofrunParser
to functions of typea -> a
. That's why we want to definerunParser
as:This CPS-style is a very different approach to parsing than what is presented in Monadic Parsing in Haskell