If you search for pop(), you can see that
self.expected_ret.append(ret)
self.generic_visit(n)
self.expected_ret.pop()
and self.push(narrows_true); [self.visit(s) for s in n.body]; self.pop()
self.push(narrows_false); [self.visit(s) for s in n.orelse]; self.pop()
In the functional style, you just pass a param using the stack, rather than using an explicit stack.It's not so bad here, but with a big enough language, and more complicated algorithms, the mutable member variables basically become "mutable globals".
And if you re-call visit() at arbitrary depths, IMO the algorithm gets obscured.
---
That said, I agreed here that visitors are useful when you need to say traverse all string literals in an AST, at arbitrary depths: https://lobste.rs/s/jdgjjt/visitor_pattern_considered_pointl...
---
A sign that this issue isn't settled is that two of the more complex type checkers make opposite decisions
- MyPy uses visitors extensively - https://github.com/python/mypy/tree/master/mypy
- TypeScript mostly uses switch/case functions - https://github.com/microsoft/TypeScript/blob/main/src/compil...
I'd be interested in analysis of why that is, but I suspect it's mainly style