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

Allow Singleton types in Union types #1551

Open
odersky opened this Issue Oct 2, 2016 · 4 comments

Comments

Projects
None yet
5 participants
@odersky
Contributor

odersky commented Oct 2, 2016

#1550 disallows singleton types in union types to avoid the problems uncovered by #829. But it would be good to allow them, if we can find a solid solution.

The current scheme is: (1) singleton types in unions are not allowed. (2) Therefore, when taking the lub, we widen every singleton type. Thats simple and consistent, but also restrictive.

@smarter

This comment has been minimized.

Show comment
Hide comment
@smarter

smarter Oct 11, 2016

Member

Note that singleton types in unions can still slip through the cracks:

scala> def or[T](x: T | Int) = x 
or: [T](x: T | Int)T | Int
scala or["foo"]("bar") 
res13: String | Int = bar

It's probably not worth trying to disallow this case before we try to make singleton types in unions actually work.

Member

smarter commented Oct 11, 2016

Note that singleton types in unions can still slip through the cracks:

scala> def or[T](x: T | Int) = x 
or: [T](x: T | Int)T | Int
scala or["foo"]("bar") 
res13: String | Int = bar

It's probably not worth trying to disallow this case before we try to make singleton types in unions actually work.

@propensive

This comment has been minimized.

Show comment
Hide comment
@propensive

propensive May 18, 2017

I uncovered another valid -- I think -- use-case while experimentitng with enums, which doesn't involve singleton literal types.

This is intended to be a linked-list-like datastructure, but with alternating types, and it might make a good test case, but currently doesn't compile due to the restriction.

enum Fence[+T, +S] {
  case End
  case Post(value: T, next: Panel[T, S] | End.type)
  case Panel(value: S, next: Post[T, S])
}

import Fence._
val fence = Post(1, Panel("two", Post(3, End)))

propensive commented May 18, 2017

I uncovered another valid -- I think -- use-case while experimentitng with enums, which doesn't involve singleton literal types.

This is intended to be a linked-list-like datastructure, but with alternating types, and it might make a good test case, but currently doesn't compile due to the restriction.

enum Fence[+T, +S] {
  case End
  case Post(value: T, next: Panel[T, S] | End.type)
  case Panel(value: S, next: Post[T, S])
}

import Fence._
val fence = Post(1, Panel("two", Post(3, End)))
@soronpo

This comment has been minimized.

Show comment
Hide comment
@soronpo

soronpo Feb 6, 2018

Contributor

Would have been nice to do:
type Binary = 0 | 1
There are other ways to achieve this constraint, though.

Contributor

soronpo commented Feb 6, 2018

Would have been nice to do:
type Binary = 0 | 1
There are other ways to achieve this constraint, though.

@panacekcz

This comment has been minimized.

Show comment
Hide comment
@panacekcz

panacekcz Oct 13, 2018

Just a curiosity, the or["foo"]("bar") example doesn't compile anymore, but it is still possible to get unsoundness with literal types in a union.

object Get2As1 {
  class OrIntroFn[T, U, TU >: T|U]{
    type V = TU
    def tuToV(): (TU => V) = p => p
  }
  class Or11X[X] extends OrIntroFn[1&1, X, (1&1|X)]{
    def get2as11X:V = tuToV()(2)
  }
  class Or11Nothing extends Or11X[Nothing]
  def get2as1:1 = new Or11Nothing().get2as11X

  def main(a:Array[String]) = {
    println(get2as1) // prints 2
    val one:1 = get2as1
    println(one) // prints 1
  }
}

panacekcz commented Oct 13, 2018

Just a curiosity, the or["foo"]("bar") example doesn't compile anymore, but it is still possible to get unsoundness with literal types in a union.

object Get2As1 {
  class OrIntroFn[T, U, TU >: T|U]{
    type V = TU
    def tuToV(): (TU => V) = p => p
  }
  class Or11X[X] extends OrIntroFn[1&1, X, (1&1|X)]{
    def get2as11X:V = tuToV()(2)
  }
  class Or11Nothing extends Or11X[Nothing]
  def get2as1:1 = new Or11Nothing().get2as11X

  def main(a:Array[String]) = {
    println(get2as1) // prints 2
    val one:1 = get2as1
    println(one) // prints 1
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment