(no title)
thosakwe | 2 years ago
1. Being within the same monad. For example, you can `bind` a `Parser a` to a function only if it returns `Parser b`.
2. Performing an actual action and breaking it out of its monad.
For example, if you're using the Parsec library, and you have a `Parser Int`, you can't get to that int without using a function like `parse`, performs the actual action of parsing input text.
With a functor, you can compose a `Parser a` within an `a -> b` function, instead of having to return `Parser b` in your function.
So if you have a `Parser Int`, and you want to turn it into a parser that multiplies its parsed input by 2, you can write `fmap (*2) myParser`, instead of having to write `myParser >>= \a -> return (a * 2)`.
Parsers being functors means it's easier to compose them with other things, without having to actually perform the parse until you need to.
Joker_vD|2 years ago
> without having to actually perform the parse until you need to.
I'd rather parse my input before starting semantic analysis on it. Although if you really want to have a one-pass translator, literally being a composition three functions, `parse`, `analyze`, `emit`, with no visible intermediary structures then yeah, this allows for it.
But I'd argue that's more complicated than having three non-entwined passes with explicit, designed data structures serving as interfaces between them instead of invisibly unevaluated thunks; not to mention the sheer complexity of doing everything in one go (why do people even claim the single-pass compilers are simple?)
thosakwe|2 years ago
Honestly, if I were writing a compiler, I would go with your approach as well.
viraptor|2 years ago
thosakwe|2 years ago
This isn't a problem if you're the one writing the function, but if you're using a 3rd party library that doesn't know about your monad, then fmap can be very useful.