Skip to content

Check that union types do not contain singleton types. #888

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
wants to merge 1 commit into from

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Oct 30, 2015

Review by @smarter. Replaces #885.

@smarter
Copy link
Member

smarter commented Oct 30, 2015

I'm not a fan of this because it reduces expressiveness. You said "If we want lubs to widen singleton types we have to disallow union types consisting of singleton types" and I believe that lub should not widen by default. Instead if we know that we need to widen at a certain point, we can do it explicitly.

On a more practical level, just disallowing singleton types in unions does not fix everything, consider:

case class Sing[T <: Singleton]()

object Test {
  def oneOrTwo(x: Sing[1] | Sing[2]): Sing[1] | Sing[2] = x
  def test: Unit = {
    val foo: Sing[3] | Sing[4] = Sing[1]()
    oneOrTwo(foo)
  }
}

This compiles fine even with this pull request, because we distribute the | and call lub while doing so. Compiling with -Xprint-types shows that Sing[1] | Sing[2] is "simplified" as Sing[_ <: Int]

@odersky
Copy link
Contributor Author

odersky commented Oct 30, 2015

I agree insofar as subtypes of Singleton should also be excluded.

@smarter
Copy link
Member

smarter commented Oct 30, 2015

But Sing[1] is not a subtype of Singleton, it's just a type parameterized by a singleton (I could have written case class Sing[T]() instead of case class Sing[T <: Singleton]() and obtained the same result). It seems that what needs to be excluded is any type where a part of the type could be a singleton type.

@smarter
Copy link
Member

smarter commented Oct 30, 2015

Here's another way to sneak past the check:

object Test {
  type Or[A, B] = A | B
  def test: Unit = {
    val foo: Or[1, 2] = 1
    val bar: Or[3, 4] = foo // should not compile but it does: both types are simplified to Int
  }
}

@odersky
Copy link
Contributor Author

odersky commented Oct 30, 2015

These examples are good food for thought. It's tricky. We do not want to allow large unions of singleton types, because they mess up performance and error diagnostics. What's a water tight way to avoid them?

@DarkDimius
Copy link
Contributor

These examples are good food for thought. It's tricky. We do not want to allow large unions of singleton types, because they mess up performance and error diagnostics. What's a water tight way to avoid them?

Could we allow them under language import? I would prefer this code to not compile

@odersky
Copy link
Contributor Author

odersky commented Oct 31, 2015

I think we have to go back to the drawing board on this one.

@odersky odersky closed this Oct 31, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants