Hello,
In a project, I have some universal type Object
for which conversion functions to “more concrete” types can be implemented. Parsers, in a sense.
A basic parser into type a
would be a function of type Object -> Maybe a
, but so these can be combined, a Parser a
is really a [Object -> Maybe a]
where, when executed on some Object
, the first Just
value “wins” (see code below). This allows for definitions like
intParser :: Parser Int = _
floatParser :: Parser Float = _
intOrFloatParser :: Parser (Either Int Float)
intOrFloatParser = fmap Left intParser <|> fmap Right floatParser
Note in the above I used fmap
(Functor
) and <|>
(Alternative
). Indeed, instances for these typeclasses seem quite straight-forward, and I believe the implementations are law-abiding. However, Alternative
has an Applicative
constraint, and that’s where I’m stuck: where pure
is easy to define, I’m unable to figure out what a sensible implementation of <*>
(or liftA2
) would look like, given the semantics of a Parser
(and what the effects of such implementation would be). Similarly, a MonadPlus
instance seems straight-forward given the Alternative
instance, but there as well I don’t see what Monad
's >>=
would be.
Does the [T -> Maybe a]
type ring any bells, and/or does anyone see if/which sensible implementations for <*>
and >>=
could be provided?
For reference, some code to make the idea more concrete:
module Parser where
import Control.Applicative (Alternative(..))
import Control.Monad (MonadPlus(..))
import Data.Monoid (First(..))
data Object
-- | A 'Parser a' is a list of functions which may be able to convert an
-- 'Object' into an 'a'. When executed using 'runParser', the first
-- successful conversion result is returned.
newtype Parser a = Parser [Object -> Maybe a]
runParser :: Parser a -> Object -> Maybe a
runParser (Parser a) obj = getFirst $ foldMap (First . ($ obj)) a
instance Functor Parser where
fmap f (Parser a) = Parser $ fmap (fmap f .) a
instance Applicative Parser where
pure a = Parser $ pure (const $ Just a)
a <*> b = _ -- ???
instance Alternative Parser where
empty = Parser mempty
Parser a <|> Parser b = Parser (a <> b)
instance Monad Parser where
a >>= k = _ -- ???
instance MonadPlus Parser where
-- Could rely on default implementations, but to be more explicit...
mzero = empty
mplus = (<|>)
instance Semigroup (Parser a) where
(<>) = (<|>)
instance Monoid (Parser a) where
mempty = empty
Thanks in advance!