ceylon icon indicating copy to clipboard operation
ceylon copied to clipboard

Allow cases where a constant label refers to an enumerated value

Open CeylonMigrationBot opened this issue 12 years ago • 17 comments

[@FroMage] I'd like to be able to switch on more than just literals, for example constants, like Java enums. Currently we can only switch on types or literals, but that's really bad because we can't switch on constants, which would lead to duplication that using constants would avoid.

For example:

String element = "foo";
void f(String val){
 switch(val)
 case(element) { ... }
}
void g(String val){
 switch(val)
 case(element) { ... }
}

As opposed to the worse:

String element = "foo";
void f(String val){
 switch(val)
 case("foo") { ... }
}
void g(String val){
 switch(val)
 case("foo") { ... }
}

Also ATM we can't switch on Java enums. I guess if we do allow constants then we will be able to fix that. If not, we should think of a way to allow that somehow.

[Migrated from ceylon/ceylon-spec#938]

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@quintesse] Agreed. Although given the fact that for literals we don't/can't check for complete coverage of values anyway (like we do for enumerated types) why do we even need a limitation on literals/constants? Is there some special reason why we can't just allow any expression (eg variables and method calls)

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@tombentley] @quintesse the idea is that the cases should be disjoint. Literals are allowed because the typechecker can prove their disjointness. It cannot do that for arbitrary expressions. It can't even do this for the reasonable-seeming case that @FroMage shows above because the value of element could change at a later point to be the same as a value in another case.

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@tombentley] As for Java enums in particular I don't understand why the model loader doesn't tell the typechecker that the enum itself is an enumerated class of its elements, and tell it that the elements are anonymous classes. Then it should work naturally, no?

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@quintesse] @tombentley Sure, disjointedness is probably nice to have, but for @FroMage example it could work, right? It would just have to be non-variable non-formal?

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@FroMage]

Literals are allowed because the typechecker can prove their disjointness

Some constants can be loaded by the typechecker to prove this. Java does this for primitives, strings and types for example, as well as enums.

FTR I don't always need disjointness, especially given the intuitive first-fit semantics.

It's just that this sort of code is disgusting:

if(status == SSLEngineResult.Status.\iBUFFER_OVERFLOW){
  ...
}else if(status == SSLEngineResult.Status.\iBUFFER_UNDERFLOW){
  ...
}else if(status == SSLEngineResult.Status.\iCLOSED){
  ...
}else if(status == SSLEngineResult.Status.\iOK){
  ...
}

I don't understand why the model loader doesn't tell the typechecker that the enum itself is an enumerated class of its elements, and tell it that the elements are anonymous classes.

Well, perhaps because that would not be true? Enum values are not instances of anonymous classes (though they can be).

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@FroMage]

Enum values are not instances of anonymous classes

We can however assume they are disjoint instances.

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@tombentley] @quintesse I didn't mean "change" as in variable, I meant "change" as in recompile. If the constant is defined in different compilation unit to the switch then if the value of the constant changes the analysis of the switch statement becomes invalid.

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@FroMage]

the value of the constant changes the analysis of the switch statement becomes invalid

The same is true of enumerated types if you don't recompile dependencies properly, this is not special to this case.

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@tombentley]

I don't understand why the model loader doesn't tell the typechecker that the enum itself is an enumerated class of its elements, and tell it that the elements are anonymous classes.

Well, perhaps because that would not be true? Enum values are not instances of anonymous classes (though they can be).

Forgive me, I meant "anonymous" in the Ceylon sense (i.e. object).

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@FroMage] Well, that's the same isn't it? They are not anonymous types.

Though I have to say, it's not entirely clear to me why that would be something we couldn't lie about. I don't see any reason why we couldn't pretend that they are anonymous types.

CeylonMigrationBot avatar Feb 17 '14 13:02 CeylonMigrationBot

[@tombentley]

They are not anonymous types.

Why not? They are values (and types, but the type can't be used where a type name is required) and they can't be subclassed.

CeylonMigrationBot avatar Feb 17 '14 14:02 CeylonMigrationBot

[@FroMage] Well, they are not anonymous types. But I agree that so far I don't have a good reason why we can't pretend that they are. If that would solve the problem, then I guess we should do it.

CeylonMigrationBot avatar Feb 17 '14 14:02 CeylonMigrationBot

[@tombentley]

the value of the constant changes the analysis of the switch statement becomes invalid

The same is true of enumerated types if you don't recompile dependencies properly, this is not special to this case.

I'm not sure they're really the same. I can see that recompiling the enumerated type could break a switch over it giving you an exception (e.g. ClassNotFoundException, NoSuchMethodError, or maybe the ceylon EnumeratedTypeError). But it with the changing constants scenario you can simply end up having two supposedly distinct constants suddenly (and silently) branching to the same case. The chances are good that that will be a bug, and it won't be easily tracked down.

CeylonMigrationBot avatar Feb 17 '14 14:02 CeylonMigrationBot

[@gavinking] Note that the kind of reasoning proposed here is the same kind of reasoning proposed by #3988. Most of the work would be in the model loader which would need to expose literal values of constant toplevel attributes.

@quintesse the idea is that the cases should be disjoint. Literals are allowed because the typechecker can prove their disjointness. It cannot do that for arbitrary expressions.

Note that even Java imposes this restriction. You can only have cases which are provably disjoint strings, that is, literals or refs to final variables which name a literal string.

CeylonMigrationBot avatar Feb 17 '14 18:02 CeylonMigrationBot

[@FroMage]

Most of the work would be in the model loader which would need to expose literal values of constant toplevel attributes

Well, I guess we can do that. For what types would you want this? I'm pretty sure we can't get it for every type.

CeylonMigrationBot avatar Feb 17 '14 19:02 CeylonMigrationBot

[@tombentley] we should now support switching on Java enums, as the model loader gives the values an anonymous (in the Ceylon sense) type (that extends the enum type).

@gavinking I had to make a very minor change to the typechecker to support this, I hope that's OK.

@FroMage for the other cases you're on your own.

CeylonMigrationBot avatar Apr 08 '14 15:04 CeylonMigrationBot

I have edited the issue description since we've already supported switching on Java enums for a long time now.

gavinking avatar Sep 16 '18 11:09 gavinking