"Bad"
lol
https://craftinginterpreters.com/representing-code.html#the-...
I remember reading through it and not understanding why it had to be this complicated and then just used a tagged union instead.
Maybe I'm too stupid for OO. But I think that's kind of the point of the grug article as well. Why burden ourselves with indirection and complexity when there's a more straight forward way?
https://prog2.de/book/sec-java-expr-problem.html - Not the writeup I was looking for but seems to cover it well.
> Why burden ourselves with indirection and complexity when there's a more straight forward way?
Because each way has its own tradeoffs that make it more or less difficult to use in particular circumstances.
https://homepages.inf.ed.ac.uk/wadler/papers/expression/expr... - Wadler's description of the expression problem.
https://grugbrain.dev/#grug-on-parsing
but the visitor pattern is nearly always a bad idea IMO: you should just encode the operation in the tree if you control it or create a recursive function that manually dispatches on the argument type if you don't
Given the syntax tree example from Wikipedia, I think I'd call it AstWalker, AstItem::dispatch(AstWalker) and AstWalker::process(AstItem) instead of Visitor, AstItem::accept(AstVisitor) and AstVisitor::visit(AstItem).
"The walker walks the AST, each items sends it to the next ones, and the walker processes them". That means something. "The visitor visits the AST items, which accept it" means basically nothing. It's more general, but also contains very little useful information. So the visitor might need different names in different situations. Fine. Just add a comment "visitor pattern" for recognizability.
I remember a situation where I needed to walk two object trees for a data comparison and import operation. I created an AbstractImporter that walked the two trees in lockstep in a guaranteed order and invoked virtual methods for each difference. It had a non-virtual doImport() for the ordered data walk, and doImport() called virtual methods like importUserAccount(), importUserAccountGrouMemberships() etc. There were two subclasses of AbstractImporter: ImportAnalyzer collected differences to display them, then there was a selection step implemented by a kind of list model + a bit of controller logic, then an ImportWorker to make the selected changes. All rather specific terminology and not exactly the visitor pattern.
However, this is just not something that I typically perceive as a problem. For example in the book that I mentioned above, I didn't feel the need to use it at all. I just added the fields or the functions that were required.
In the first link you provided, the OCaml code seems to use unions as well (I don't know the language). I assume OCaml checks for exhaustive matching, so it seems extremely straight forward to extend this code.
On the other hand I have absolutely no issues with a big switch case in a more simple language. I just had a look at the code I wrote quite a while ago and it looks fine.
In languages influenced by ML (like contemporary Java!) it is common in compiler work in that you might have an AST or similar kind of structure and you end up writing a lot of functions that use pattern matching like
switch(node) {
type1(a,b) -> whatever(a,b)
type2(c) -> process(c)
}
to implement various "functions" such as rewriting the AST into bytecode, building a symbol table, or something. In some cases you could turn this inside out and put a bunch of methods on a bunch of classes that do various things for each kind of node but if you use pattern matching you can neatly group together all the code that does the same thing to all the different objects rather than forcing that code to be spread out on a bunch of different objects.https://blog.scottlogic.com/2025/01/20/algebraic-data-types-...
Stands to reason. Rust "enums" are tagged unions (a.k.a. sum types, discriminated unions).
In implementation, the tag, unless otherwise specified, is produced by an enum, which I guess is why it got that somewhat confusing keyword.
The additional complexity doesn't add significant value IMO. I admit that's a subjective claim.