Reputation: 1229
I look at this declaration:
newtype Parser a = Parser { parse :: String -> Maybe (a,String) }
Here is what I understand:
1) Parser is declared as a type with a type parameter a
2) You can instantiate Parser by providing a parser function for example p = Parser (\s -> Nothing)
What I observed is that suddenly I have a function name parse
defined and it is capable of running Parsers.
For example, I can run:
parse (Parser (\s -> Nothing)) "my input"
and get Nothing
as output.
How was this parse function got defined with this specific signature? How does this function "know" to execute the Parser given to it? Hope that someone can clear my confusion.
Thanks!
Upvotes: 4
Views: 1006
Reputation: 8467
First, let’s have a look at a parser newtype without record syntax:
newtype Parser' a = Parser' (String -> Maybe (a,String))
It should be obvious what this type does: it stores a function String -> Maybe (a,String)
. To run this parser, we will need to make a new function:
runParser' :: Parser' a -> String -> Maybe (a,String)
runParser' (Parser' p) i = p i
And now we can run parsers like runParser' (Parser' $ \s -> Nothing) "my input"
.
But now note that, since Haskell functions are curried, we can simply remove the reference to the input i
to get:
runParser'' :: Parser' a -> (String -> Maybe (a,String))
runParser'' (Parser' p) = p
This function is exactly equivalent to runParser'
, but you could think about it differently: instead of applying the parser function to the value explicitly, it simply takes a parser and fetches the parser function from it; however, thanks to currying, runParser''
can still be used with two arguments.
Now, let’s go back to back to your original type:
newtype Parser a = Parser { parse :: String -> Maybe (a,String) }
The only difference between your type and mine is that your type uses record syntax, although it may be a bit hard to recognise since a newtype
can only have one field; this record syntax automatically defines a function parse :: Parser a -> (String -> Maybe (a,String))
, which extracts the String -> Maybe (a,String)
function from the Parser a
. Hopefully the rest should be obvious: thanks to currying, parse
can be used with two arguments rather than one, and this simply has the effect of running the function stored within the Parser a
. In other words, your definition is exactly equivalent to the following code:
newtype Parser a = Parser (String -> Maybe (a,String))
parse :: Parser a -> (String -> Maybe (a,String))
parse (Parser p) = p
Upvotes: 7
Reputation: 1220
When you write newtype Parser a = Parser { parse :: String -> Maybe (a,String) }
you introduce three things:
A type named Parser
.
A term level constructor of Parser
s named Parser
. The type of this function is
Parser :: (String -> Maybe (a, String)) -> Parser a
You give it a function and it wraps it inside a Parser
parse
to remove the Parser
wrapper and get your function back. The type of this function is:parse :: Parser a -> String -> Maybe (a, String)
Check yourself in ghci
:
Prelude> newtype Parser a = Parser { parse :: String -> Maybe (a,String) }
Prelude> :t Parser
Parser :: (String -> Maybe (a, String)) -> Parser a
Prelude> :t parse
parse :: Parser a -> String -> Maybe (a, String)
Prelude>
It's worth nothing that the term level constructor (Parser
) and the function to remove the wrapper (parse
) are both arbitrary names and don't need to match the type name. It's common for instance to write:
newtype Parser a = Parser { unParser :: String -> Maybe (a,String) }
this makes it clear unParse
removes the wrapper around the parsing function. However, I recommend your type and constructor have the same name when using newtypes
.
How does this function "know" to execute the Parser given to it
You are unwrapping the function using parse
and then calling the unwrapped function with "myInput"
.
Upvotes: 8