I don’t know if it has a formal name, but I’ve generally imagined that a lot of the point of a strong typing system is to make representing valid values straightforward and to make representing invalid values impossible. This is the opposite — valid values are nontrivial — I would have to encode the position by hand, which makes any use of, say, fmap quite messy because I need to either define a more complicated functor (and maybe make consuming a subset of the parse tree and tracking that fact impossible) or manually pass position context through. And invalid values are all to easy to represent: the context could fail to match the returned string length or the returned string could fail to be a suffix.
You're having a /r/restofthefuckingowl moment, and I understand the reaction, but calm down :D
There is a little wiggle-room for error, down at the 'unit' level. Here's a parser (based on the above Parser type) which consumes input as long as its predicate matches:
parseWhile :: (Char -> Bool) -> Parser String
parseWhile pred = P $ \s -> do
let some = takeWhile pred s
rest = dropWhile pred s
Just (some, rest)
It's verbose, and maybe there's a bug in it? (Side note: it also needs to step through s twice, so it's dumb). Here's a more compact version without the 2 * work: parseWhile' :: (Char -> Bool) -> Parser String
parseWhile' pred = P $ Just . span pred
No type-systemy category-theoretical nonsense will stop me from getting a boolean the wrong way around. Maybe I accidentally implemented skipWhile or parseUntil without realising.So what's the point?
* You build bigger parts out of smaller parts *
Here's the actual parser for my if-then-else expression in my language:
parseIfThenElse :: Parser ParseState (Expr Untyped ByteString)
parseIfThenElse = do
p <- token TIf *> parseExpr
t <- token TThen *> parseExpr
f <- token TElse *> parseExpr
pure $ IfThenElse Untyped p t f
No manual plumbing or passing in the above code. What about one step up? Here's code which calls the above: parseNonApply :: Parser ParseState (Expr Untyped ByteString)
parseNonApply = parseLet
<|> parseIfThenElse
<|> parseLambda
<|> parseTerm
<|> parseNegated
<|> parseShown
<|> parseErr
<|> parseParen