First, just some quick context. I'm going through the Haskell Programming From First Principles book, and ran into the following exercise.
Try writing a Parser that does what
string
does, but usingchar
.
I couldn't figure it out, so I checked out the source for the implementation. I'm currently trying to wrap my head around it. Here it is:
class Parsing m => CharParsing m where
-- etc.
string :: CharParsing m => String -> m String
string s = s <$ try (traverse_ char s) <?> show s
My questions are as follows, from most to least specific.
Why is
show
necessary?Why is
s <$
necessary? Doesn'ttraverse char s <?> s
work the same? In other words, why do we throw away the results of the traversal?What is going on with the traversal? I get what a list traversal does, so I guess I'm confused about the Applicative/Monad instances for Parser. On a high level, I get that the traversal applies
char
, which has typeCharParsing m => Char -> m Char
, to every character in strings
, and then collects all the results into something of typeParser [Char]
. So the types make sense, but I have no idea what's going on in the background.
Thanks in advance!
Because
show
ing a string (or aText
, etc.) escapes special characters, which makes sense for error messages:The result of the parse is unnecessary because we know in advance that it would be
s
(if the parse were successful).traverse
would needlessly reconstructs
from the results of parsing each individual character. In general, if the results are not needed it is a good idea to usetraverse_
(which just combines the effects, discarding the results without trying to rebuild the data structure) rather thantraverse
, so that is likely why the function is written the way it is.traverse_ char s
(traverse_
, and nottraverse
, as explained above) is a parser. It tries to parse, in order, each character ins
, while discarding the results, and it is built by sequencing parsers for each character ins
. It may be helpful to remind thattraverse_
is just a fold which uses(*>)
: