From reading this answer, I understand that the inclusion of empty
in Alternative
was largely a design decision to make Alternative
a monoid (and thus more powerful). It seems to me this was also because otherwise you couldn't express any laws for Alternative
.
But this is a pain if I have a generic applicative parser like so:
newtype Parser err src target = Parser (ExceptT err (State [src]) target)
deriving (Functor, Applicative, Alternative, Monad, MonadState [src], MonadError err)
Clearly, we can get the same behavior as <|>
and many
/some
from Control.Applicative
with:
option :: Parser e s t -> Parser e s t -> Parser e s t
option parserA parserB = do
state <- get
parserA `catchError` \_ -> put state >> parserB
many :: Parser e s t -> Parser e s [t]
many parser = some parser `option` return []
some :: Parser e s t -> Parser e s [t]
some parser = (:) <$> parser <*> many parser
Even though none of these use empty
, it seems like I am forced to re-implement them instead of deriving Alternative
, because I can't conceive a generic way to instance empty
for it (of course, I'd still need to instance <|>
to get the preservation of state
on parserA
error, but then I could get some
, many
, optional
and friends for free).
Digging into Parsec's source, it seems that it subverts this because it doesn't allow for custom error types (or at least, Parsec
isn't parameterized by a custom error type):
instance Applicative.Alternative (ParsecT s u m) where
empty = mzero
(<|>) = mplus
instance MonadPlus (ParsecT s u m) where
mzero = parserZero
mplus p1 p2 = parserPlus p1 p2
-- | @parserZero@ always fails without consuming any input. @parserZero@ is defined
-- equal to the 'mzero' member of the 'MonadPlus' class and to the 'Control.Applicative.empty' member
-- of the 'Control.Applicative.Alternative' class.
parserZero :: ParsecT s u m a
parserZero
= ParsecT $ \s _ _ _ eerr ->
eerr $ unknownError s
unknownError :: State s u -> ParseError
unknownError state = newErrorUnknown (statePos state)
newErrorUnknown :: SourcePos -> ParseError
newErrorUnknown pos
= ParseError pos []
Taking inspiration from this, it seems like the only reasonable way around this is to have my generic parser wrap the user error type with something like:
data ParserError err = UserError err | UnknownError
newtype Parser err src target = Parser (ExceptT (ParserError err) (State [src]) target)
deriving (Functor, Applicative, Alternative, Monad, MonadState [src], MonadError err)
And then empty
can be:
empty = throwError UnknownError
This just feels wrong, though. This wrapping only exists to appease the requirement for an empty
and it makes consumers of this generic parser do more work to handle errors (they also must handle UnknownError
now and unwrap their custom errors). Is there any way to avoid this?