Skip to content

Enum Value singleton fails to reduce in match type #10511

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
bishabosha opened this issue Nov 26, 2020 · 10 comments · Fixed by #10938
Closed

Enum Value singleton fails to reduce in match type #10511

bishabosha opened this issue Nov 26, 2020 · 10 comments · Fixed by #10938

Comments

@bishabosha
Copy link
Member

bishabosha commented Nov 26, 2020

Minimised Code

enum Bool {
  case True
  case False
}

import Bool._

type Not[B <: Bool] = B match {
  case True.type => False.type
  case False.type => True.type  
}

val f: Not[False.type] = True

Output

13 |val f: Not[False.type] = True
   |                         ^^^^
   |                         Found:    (Bool.True : Bool)
   |                         Required: Not[(Bool.False : Bool)]

Expectation

if I replace enum Bool with a sealed trait and case objects then the code compiles:

+sealed trait Bool
+object Bool {
+  case object True extends Bool
+  case object False extends Bool
-enum Bool {
-  case True
-  case False
 }
...

Originally posted by @bishabosha in #10510 (comment)

@bishabosha bishabosha changed the title Enum Value singleton type will not reduce in Enum Value singleton fails to reduce in match type Nov 26, 2020
@OlivierBlanvillain
Copy link
Contributor

if I replace enum Bool with a sealed trait and case objects then the code compiles:

That's not very surprizing given that this enum doesn't create new types for its cases, just vals.

The question here is what's the meaning of the patterns in the following example:?

def not[B <: Bool](b: B) = b match {
  case _: True.type => False.type
  case _: False.type => True.type  
}

Given that True and False are vals, _: True.type and _: False.type desugar to calls to ==, which we know nothing at compile time, so there is nothing we can do on the match type side.

@OlivierBlanvillain OlivierBlanvillain removed their assignment Dec 9, 2020
@OlivierBlanvillain
Copy link
Contributor

I think this is another example that shows that users don't read the enum spec and are surprised by the differences between case True() and case True in an enum. In my opinion it would be better to desugar case True to a case object, but we already had this discussion before.

@bishabosha
Copy link
Member Author

bishabosha commented Dec 9, 2020

@OlivierBlanvillain matching on singleton types becomes a test on identity with eq, not with ==, so that is disjoint in the case of enum values

@OlivierBlanvillain
Copy link
Contributor

OlivierBlanvillain commented Dec 9, 2020

Yes my bad, but in the general case there is no way to statically conclude that a.eq(b) evaluates to false for two vals a and b since they could point to the same object. By the time the Not match type is reduced, the Bool enum is indistinguishable from the following:

sealed trait Bool
object Bool {
  val True = new Bool
  val False = True
}

@bishabosha
Copy link
Member Author

@OlivierBlanvillain all symbols of enum value definitions will also have the EnumValue flag, is it possible to check for that? (i guess then they would have to be distinguished by name in case one is a cloned symbol)

@odersky
Copy link
Contributor

odersky commented Dec 26, 2020

@OlivierBlanvillain @bishabosha Not sure where we are with this. Is there a concrete action item? if not please close.

@OlivierBlanvillain
Copy link
Contributor

Even if what @bishabosha suggests is technically feasible, I think that it would a deviation from the current design of enums and match types. An enum defines values, not types, so from an ideological standpoint, users interested in doing type-level programming should defines their types using traits and classes, not enums.

@bishabosha
Copy link
Member Author

An enum defines values, not types, so from an ideological standpoint, users interested in doing type-level programming should defines their types using traits and classes, not enums.

I am confused about this point, all val definitions define a type: their singleton type.

we already have a case in the Inliner to support treating enum values as defining a unique type, used in inline match:
https://github.com/lampepfl/dotty/blob/1ab80f78b1177290654f7935e8c571e27141b7e3/compiler/src/dotty/tools/dotc/typer/Inliner.scala#L349

bishabosha added a commit to dotty-staging/dotty that referenced this issue Dec 28, 2020
bishabosha added a commit to dotty-staging/dotty that referenced this issue Dec 28, 2020
bishabosha added a commit to dotty-staging/dotty that referenced this issue Dec 28, 2020
bishabosha added a commit to dotty-staging/dotty that referenced this issue Dec 28, 2020
bishabosha added a commit to dotty-staging/dotty that referenced this issue Dec 28, 2020
@OlivierBlanvillain
Copy link
Contributor

I am confused about this point, all val definitions define a type: their singleton type.

The meaning of case _: T changes depending on T, if T is a ground type that type test evaluates using a .isInstanceOf[T], whereas when T is a singleton type of form v.type, the type test evaluates using a .eq(v). The fact that enum Bar { case Foo } does not define a non singleton type for Foo thus makes a huge difference for match types.

Enum were designed to be a desugaring step which makes them super simple; if we have to special case the type checker at every corner to handle enums we lose some of that simplicity. I'm not saying we shouldn't do it, but it falls into the needs-spec/feature-request category. (Feel free to reopen if you're not convinced, I don't want to impose my views!)

@bishabosha
Copy link
Member Author

bishabosha commented Dec 29, 2020

@odersky I will reopen due to the linked PR #10938 which enables the snippet in this issue to compile.

I agree that not having a new backing class for each singleton enum value causes some friction but then again I fail to see why we could promote enum as a drop in replacement if it were not able to participate in the key metaprogramming advances of Scala 3.

OlivierBlanvillain added a commit that referenced this issue Jan 11, 2021
fix #10511: compare enumvalues in provably disjoint
@Kordyjan Kordyjan modified the milestones: 3.0.0-RC1, 3.0.0 Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants