top | item 37739385

(no title)

thosakwe | 2 years ago

Yes, but if it we're talking about parser monads, then usually you can't apply a function directly to a parser's result without either:

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.

discuss

order

Joker_vD|2 years ago

Right, so you're essentially talking about how in e.g.

    class Parser:
        def __init__(self, text):
            self.text = text
            self.result = None
            # other inners state/context fields

        def parse(self):
            self.parse_top_level()
            return self.result

    def parse(text):
        return Parser(text).result
the free function "parse" throws away the inner context. When one uses parser combinators, there is no single monolithic Parser class which one may extend with whatever additional methods necessary (and maybe take some free functions as callbacks, why not); instead there are lots of smaller functions returning the leftover contexts together with the results, and you have to combine them somehow, together with free functions.

> 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

You make a valid point about the added complexity of combining parser combinators with functors and other concepts.

Honestly, if I were writing a compiler, I would go with your approach as well.

viraptor|2 years ago

I don't get the last part of the comment. `myParser >>= \a -> return (a * 2)` doesn't have to parse immediately, right? You could push that function at the end of a list and only apply it during `parse` anyway.

thosakwe|2 years ago

Correct, that function doesn't have to parse immediately, but it does have to be aware your monad exists.

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.