(<|>) :: Alternative f => f a -> f a -> f a
(<*) :: Applicative f => f a -> f b -> f a
(*>) :: Applicative f => f a -> f b -> f b
(<|>)
lets us pick whichever of left and right is successful(<*)
lets us evaluatef a
andf b
returning only the result off a
(it's strict inf b
andf b
)(*>)
is(<*)
the opposite of(<*)
- all of these are in
Control.Applicative
Think of a cursor moving through a string-like object. Parser combinators tell the cursor when to move and what to do with what it encounters.
Example:
char '1'
tells the cursor "give me the character '1'".
Parser combinators themselves don't do anything -- they only describe how to do things. To do
something, we need some function (pseudocode) Parser a -> ... -> String -> Result a
. This function
frequently starts with parse
. In trifecta
, it's parseString
or parseByteString
if you're feeling
fancy. In attoparsec
it's parse
and parseOnly ('parseOnly
stops when it's done and returns an
Either
, parse
returns a Result
holding the remaining things to parse and what was successfully
parsed).
Example:
parseString (char '1') mempty :: String -> Result Char
parseString (char '1') mempty "2" :: Result Char
parseString (char '1') mempty "1" :: Result Char
(<|>)
is from the Alternative
typeclass, with the operator exposed in Control.Applicative
.
Here's what it looks like for Maybe
:
instance Alternative Maybe where
empty = Nothing
Nothing <|> r = r
l <|> _ = l
The Alternative
typeclass also provides some
and many
methods. some
repeats a parser at
least once, while many
repeats a parser any number of times (including zero):
parseString (some integer) mempty "a" -- Failure
parseString (some integer) mempty "a" -- Success []
Just don't. The parsers in Text.Trifecta.Char
are tokenizers already. If you need custom
tokenizing, worry about how to do that then.
Because parser combinators are build off of parsing typeclasses, you can have function signatures like:
parseFraction :: (Monad m, TokenParsing m) => m Rational
and maintain simultaneous support for whatever parsers anyone has written or will write now or in the future.
You can encounter the problem where you want
real world data -> YourDataType
and
YourDataType -> real world data
These are unmarshalling and marshalling, respectively.
We can do this with aeson by providing FromJSON
and ToJSON
typeclasses for
our datatype, but it's sort of boring compared to unmarshalling problems in
Advent of Code and they're autoderivable because we live in the future, so
skipping.
This isn't a very good one but here it is