Had a very similar experience to yours with one important exception. The visitor pattern is relevant for traversing ASTs in single dispatch languages. Most notably, this includes Java and C++.
The alternative concept is multiple dispatch and it is way more anti-fragile.
This is where the next step in college literally was to learn the concept of cargo-culting. The concept of multiple dispatch is considerably more useful than single dispatch.
Many years later the realization hit, that OOP,
singletons, DB mocking and monads are the hard parts
[1,2]. Then you can even skip multiple dispatch in
favor of low latency. C++ wants this but its
ecosystem has no idea (as in zero) of how to get
there [3,4,5,6] (-Ofast -fno-fast-math). Then the
"Patterns" (capital P) melt away.
On a semi-related note, it seems worth pondering whether strict typing requirements and/or a missing garbage collector make macro programming and AST work so hard in non-lisp languages. Think I read somewhere that some people considered Rust macros hard. Sounded like they were harder than they should be, which means that the language designers piled on incidental complexity. Macros are difficult enough as it is. And worth it. Even Python pilfered the "with" syntax.
The visitor pattern is relevant in multiple dispatch also; just most of its boilerplate goes away, so that only the visitation remains: traverse the AST (or whatever structure) and invoke the given method with the node and visitor as arguments.
For list in Common Lisp, the ancient mapcar function can do this:
Multiple dispatch means that a method is selected based on the run-time type of more than one argument, rather than just "the object" (leftmost argument).
This is beneficial to the Visitor Pattern, because the pattern needs to traverse a structure with polymorphic nodes, and invoke logic that is based on the type of each node, and on the type of the visiting object.
The familiar single-dispatch Visitor Pattern introduces an emulation of double dispatch via two single dispatches. First a "accept" method is invoked on each Node of the traversed structure, taking the visitor as the argument. This accept method is a stub which calls the "visit" method on the visitor, passing the node as an argument. The static type of the node is known at that point, so the visitor can statically dispatch different overloads for different nodes. I possibly have some of this backwards, but it doesn't matter; it will work with different naming.
Under multiple dispatch, we can just call a single generic function visit which is dispatched for the node and visitor in one step. If it is a printing visitor, it prints the node, and so on.
Had a very similar experience to yours with one important exception. The visitor pattern is relevant for traversing ASTs in single dispatch languages. Most notably, this includes Java and C++.
The alternative concept is multiple dispatch and it is way more anti-fragile.
This is where the next step in college literally was to learn the concept of cargo-culting. The concept of multiple dispatch is considerably more useful than single dispatch.
Many years later the realization hit, that OOP, singletons, DB mocking and monads are the hard parts [1,2]. Then you can even skip multiple dispatch in favor of low latency. C++ wants this but its ecosystem has no idea (as in zero) of how to get there [3,4,5,6] (-Ofast -fno-fast-math). Then the "Patterns" (capital P) melt away.
On a semi-related note, it seems worth pondering whether strict typing requirements and/or a missing garbage collector make macro programming and AST work so hard in non-lisp languages. Think I read somewhere that some people considered Rust macros hard. Sounded like they were harder than they should be, which means that the language designers piled on incidental complexity. Macros are difficult enough as it is. And worth it. Even Python pilfered the "with" syntax.
[1] http://somethingdoneright.net/2015/07/30/when-object-orienta...
[2] https://lobste.rs/s/vbivyq/synchronous_core_asynchronous_she...
[3] https://moyix.blogspot.com/2022/09/someones-been-messing-wit...
[4] https://news.ycombinator.com/item?id=32738206
[5] https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-do... Note, that Fortran and Functional Analysis got this right ages ago. Excuses are now few and far between. All of use are late to the party.
[6] https://news.ycombinator.com/item?id=38282950
Edit: typo