4

The Sun Compiler Tree API uses an interface called Tree as the parent of all the different kinds of parse tree nodes (assignments, if-statements, class declarations, etc.). A part of this interface is an enumeration called Kind that holds the type of the node.

I have yet to encounter a case where the enum is not faithful to the node's type as a Java object. What is the advantage gained here? Just to open the door to switch statements? What does this achieve that polymorphism cannot?

5

An enum models a finite set of different choices where you need to know which one you're dealing with before you can proceed. Dynamic dispatch solves a completely different problem - allowing for an unlimited number of options and handling them uniformly.

An enum is most likely more efficient and generally safer than inspecting the class of the node:

if (node instanceof Type1Node) {
    ...
} else if (node instanceof Type2Node) {
    ...
} else {
    throw new AssertionError("Someone added a new node type.");
}

VS

switch (node.type) {
case Tree.Kind.AND:
    ...
case Tree.Kind.AND_ASSIGNMENT:
    ...
}
throw new AssertionError("Someone modified this enum");

The enum version has only one jump and you'll get warnings if you miss any cases, either through negligence or because the enum has been updated. You can't get those warnings with a ton of instanceof tests because new subclasses can be added.

A reasonable alternative is to use some form of Visitor. But even with lambdas enabling more compact syntax for visitors, there's still advantages to a switch - you can easily merge multiple cases whenever they're handled the same way.

  • That's a really good point -- I've been hesitant to do this in my own designs, but you're absolutely right about the finite set thing. – Alex Reinking Mar 10 '15 at 16:58
3

What would be he alternative to enums?

A non-formalized integral type? That's a step backwards. Making all distinctions by dynamic dispatch on subclass methods of the node type? That only works for some problems; in languages without multiple dispatch, there are a lot of situations that you can't handle that way. If the language doesn't give a better option, switching on an enum is exactly the right thing to do.

  • Thanks for that perspective! With all I've been reading about the evils of switch and enums (and, possibly-satirically, the if statement), I guess my better judgment escaped me! – Alex Reinking Mar 10 '15 at 16:31
2

For parse trees especially, it is very uncommon to use polymorphism. It would require the base node type to know about all of the different capabilities/properties/attributes of every different node type in the parse tree. That can get out of hand quickly.

By using an enum, it makes it easier to build visitors to walk the tree and do things to it depending on what node they're visiting. Since that sort of visitation is the common usage pattern for static analysis, optimization and back end compilation, it makes sense to make that easy.

  • For parse trees especially, it is very uncommon to use polymorphism. It would require the base node type to know about all of the different capabilities/properties/attributes of every different node type in the parse tree. Why? The entire point of a polymorphic base class is that it doesn't have to know what its descendants are up to. – Mason Wheeler Mar 16 '15 at 21:19
  • @MasonWheeler - Parse trees tend to be very broad in their inheritance hierarchy. Each node has one or two properties specific to it and it alone. If you used polymorphism, you would have to push all of those weird disparate properties up to the common base class. This would allow the base class to not know what its inheritors are doing, but then it knows what all of its inheritors can do. Hopefully that's clearer... – Telastyn Mar 16 '15 at 21:25
  • Why would you have to do that? The base class doesn't need all those properties; just the ones that are common to all nodes. What needs to know about the capabilities of all the disparate descendant classes is the code that operates on the trees. I've poked around inside a handful of compilers with polymorphic ADTs, implemented in various different styles, and never seen the problem you're describing. – Mason Wheeler Mar 16 '15 at 21:28
  • @MasonWheeler - there is no common property to a node generally (except serialization perhaps). How does the code operate on the tree know what type of node it is on? Most I've seen have used enums in favor of instanceof checks. – Telastyn Mar 16 '15 at 21:36
  • There are two basic ways. One is to have the node classes themselves contain the necessary logic for the compilation process; the other is to have the base node class declare methods to implement the Visitor Pattern and have visitor classes contain the compilation logic. – Mason Wheeler Mar 16 '15 at 23:07
1

First, I believe you know this, but it may not be clear to other readers. The interface in question provides both an enum and polymorphic dispatch via the visitor pattern. Given that, I think in general you should prefer to use polymorphism, if you can do so cleanly without duplication.

I think the enums are there not necessarily to allow for switch statements, but for other simplifying structures. You can use them to index a map, for example. You could create a KindsWithConditionals array. You can store a kind enum in a database column. You can easily log it with toString. There are lots of things you can do with an enum that are cumbersome without one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.