I wanted to put an idea in the round that came up in a comment of a stackoverflow entry ( http://stackoverflow.com/questions/5645700/whats-the-difference-between-scala-and-red-hats-ceylon-language/5646305#comment-6468943 ):
We have a sort of type intersection in the form of `A with B`, so that in
def perform( x: A with B ) { ... }
we can assume the functionality of both types `A` and `B`. Wouldn't it be a very interesting idea to explore the possibility of a type *union*, like
def perform( x: A or B ) { ... }
so that the main body of that method can safely invoke anything on `x` which is common between types `A` and `B`. It would then be possible to do an exhaustive match like this:
def perform( x: A or B ) {
...
x match {
case a: A => ...
case b: B => ...
}
}
and that way in some distant version of Scala one would overcome the need to have method overloading and multiple constructors.
Although we will hopefully in this distant version have fully supported runtime generics, this could also solve the case of
def perform( a: List[ A ]) { ... }
def perform( b: List[ B ]) { ... }
under the condition that erasure is still happening, because then you could do
def perform( a: List[ A or B ]) { ... }
i know there is `Either` but that has many disadvantages against this union types -- it is extra verbose, cannot handle view bounds on its type parameters without much extra bloat of implicits work, and it cannot handle more than two types.
best, -sciss-
https://lampsvn.epfl.ch/trac/scala/ticket/3749
"We plan to address this issue in future versions of Scala by
introducing union types, so that the approximation of the least upper
bound you see here could be denoted directly (and precisely) as
java.lang.Double or java.lang.Long. [...]
First, we need to work out the nitty gritty details of the theory, though."
How often would you use that where the more general structural types wouldn't work?
`List(1.0, 2)` by the way is a good example of where this could be extremely useful, too.
best, -sciss-
--
Jim
On Fri, Apr 15, 2011 at 05:00:30PM +0100, Sciss wrote:
> Date: Fri, 15 Apr 2011 17:00:30 +0100
> From: Sciss <con...@sciss.de>
> To: scala-l...@googlegroups.com
> Subject: [scala-language] Union types
the only downsides are that the compiler cannot check the matches for exhaustiveness, that i cannot use common interface between the types, and that you need to import half a dozen imports (that always makes me slightly depressed and i think this is an example of why people keep having the impression that scala is too complicated).
best, -sciss-
The LUB of two arbitrary types will in general subsume many types that
would not otherwise be compatible with either of the two original
types, so this does not seem to me like a proper interpretation of the
union of two types.
> First, we need to work out the nitty gritty details of the theory,
> though."
Randall Schulz
I often find myself wanting an API to respond to several related
parametrized types. Some common examples:
aMethod(t: T)
aMethod(optT: Option[T])
aMethod(ts: Seq[T])
It would be nice to support this without requiring implicit conversions.
-Ben
FWIW, I was wanting the same capability myself last week, to address
the difficulty of overloading methods by parameter type, when the
types are parametrized.
I often find myself wanting an API to respond to several related
parametrized types. Some common examples:
aMethod(t: T)
aMethod(optT: Option[T])
aMethod(ts: Seq[T])
It would be nice to support this without requiring implicit conversions.
https://issues.scala-lang.org/browse/SUGGEST-22
Scala has the compound (i.e. conjunction) type by employing `with`,
but it does not have a union (i.e. disjunction) type.
I hope I have successfully demonstrated how to simulate a nearly first-
class union type:
And the negation of the disjunction:
http://www.chuusai.com/2011/06/09/scala-union-types-curry-howard/#comment-1160
I have filed today two bug reports that hinder use of that simulated
union type.
Beyond that, I am pondering if Scala is going to lose significant
competitive ground w.r.t. to Ceylon if it doesn't add a completely
first-class union type asap. And go beyond Ceylon by adding some way
to extend the disjunction (e.g. for heterogeneous types) as I
demonstrated at link above for negation.
Afaics it is necessary for supporting heterogeneous collections type-
safely without resorting to the (probably unbearable for novices, and
more "Scala is difficult to read" accusations) complexity of the
experimental metascala.
I have been thinking that first-class intersection types are very
important, both for the [reasons Ceylon has them][8], and because
instead of [subsuming][9] to `Any` which means unboxing with a `match`
on expected types can generate a runtime error, the unboxing of a
([heterogeneous collection][10] containing a) disjunction can be type
checked. Unions are more straightforward than the [complexity of using]
[11] the experimental [HList][12] of [metascala][13] for heterogeneous
collections.
[8]: http://ceylon-lang.org/documentation/1.0/faq/language-design/#optional_types
[9]: http://stackoverflow.com/questions/8360413/selectively-disable-subsumption-in-scala-correctly-type-list-contains
[10]: http://lambda-the-ultimate.org/node/4394#comment-68110
[11]: http://jnordenberg.blogspot.com/2008/08/hlist-in-scala.html
[12]: http://stackoverflow.com/questions/185972/arrays-in-scala/205729#205729
[13]: http://stackoverflow.com/questions/3508077/does-scala-have-type-disjunction-union-types/3508357#3508357
P.S. There is a duplicate syntax-highlighted version buried nearer to
the bottom of the following stackoverflow answer:
P.S.S. Consider reading the Ceylon language design FAQ[8], as it
appears to me to be significant substance in their effort, although I
am not yet clear on what corner cases may be lurking in their attempts
to simplify and make code more amenable to the mainsteam.
best, .h.h.
If my (twist on Miles Sabin's) implementation above is correct,
apparently the disjunction is the conjunction with subtyping in the
contravariant direction, with each item in the conjunction subtyped in
the contravariant direction. That appears to be a statement of De
Morgan's laws where negation is subtyping in the contravariant
direction.
Deep mixin composition redux? Like it :-)
Cheers,
Miles
--
Miles Sabin
tel: +44 7813 944 528
gtalk: mi...@milessabin.com
skype: milessabin
g+: http://www.milessabin.com
http://twitter.com/milessabin
http://www.chuusai.com/
these encoding are neat, but the union types we're working on go beyond what you can do "in user space"assume membersOf(S) = MS, membersOf(T) = MTthen, membersOf(S \/ T) = MS intersect MT, (not a typo -- the members of a union type is the intersection of the sets of members of its constituents)where the intersection is computed by including only synonymous members from both MS and MT,say val m: Ts from MS and val m: Tt from MT yield a member val m: Ts \/ Tt of the intersected set
these encoding are neat, but the union types we're working on go beyond what you can do "in user space"assume membersOf(S) = MS, membersOf(T) = MTthen, membersOf(S \/ T) = MS intersect MT, (not a typo -- the members of a union type is the intersection of the sets of members of its constituents)
where the intersection is computed by including only synonymous members from both MS and MT,say val m: Ts from MS and val m: Tt from MT yield a member val m: Ts \/ Tt of the intersected set
conversely, membersOf(S /\ T) = MS union MT (where, again, overlapping members have their types intersected)
data Ordering = EQ | LT | GT
data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Eq
data Day = Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday deriving (Enum)
etc.
(http://www.haskell.org/tutorial/stdclasses.html)
that would be for the main purpose of having them altogether. i, too, think that intersection between their members doesn't make sense... we need to be able to express Saturday | Sunday without resorting to:
sealed trait Day
sealed trait MondayOrTuesday extends Day
sealed trait MondayOrWednesday extends Day
...
case object Monday extends MondayOrTuesday with MondayOrWednesday with ...
right?
best, ..hh..
Intuition goes like follows: Imagine you have type E = S v T. If you have val e: E, then e's real type can be either S or T but you don't know which, you only know it is E, so the only members you can safely access on x are those which are in both S and in T, i.e. an intersection. And vice-versa with E = S & T, you can safely access all members in S and also in T, i.e. a union.
trait Tree
case class Leaf(x:Int) extends Tree
case class Branch(left:Tree, x:Int, right:Tree) extends Tree
(ignoring that it has to be sealed to be protected from new
alternatives to be added).
We can then do pattern matching over trees as in:
def sum(tree: Tree): Int =
tree match {
case Leaf(x) => x
case Branch(l,x, r) => sum(l) + x + sum(r)
}
In other functional programming (FP) languages we would define the
type along the following lines
(syntax varies from language to language of course, but the essence is this):
datatype Tree = Leaf(x:Int) | Branch(left: Int, right: Int)
The Scala way of repeating additional keywords:
case class ... extends ....
can be somewhat annoying. It would be nice with the shorter form.
Now, there is an argument for the longer form since it allows for
adding methods etc to the alternatives,
which the standard FP style does not. One could then consider an
alternative such as:
datatype Tree {
case class Leaf(x:Int)
case class Branch(left:Tree,x:Int, right:Tree)
}
which would for example allow us to add a toString method to each alternative:
datatype Tree {
case class Leaf(x:Int) {
def toString: String = ...
}
case class Branch(left:Tree,x:Int, right:Tree) {
def toString: String = ...
}
}
/Jesper Nordenberg
What would be the use case for this feature? I can see the use of B <: A & C <: A => (B | C) <: A, but this feature looks more like structural typing.
allowed access to the members though, how is List[Int] V List[String] handled? What does .head return? Int V String? Or would it
where the intersection is computed by including only synonymous members from both MS and MT,say val m: Ts from MS and val m: Tt from MT yield a member val m: Ts \/ Tt of the intersected set
The whole idea is really quite clever. Imagine you have:
trait Bippy {def bippyMethod(x: String): Intdef takeAction(arg: TwoWheeledVehicle): Error}
trait Wibble {def wibbleMethod(x: Int): Stringdef takeAction(arg: PoweredVehicle): Exception}
The union would then contain the intersection of the members (methods/properties). The fun part is how the parameters and return types of these members also get unified:
trait Bippy or Wibble {def takeAction(arg: TwoWheeledVehicle with PoweredVehicle): Throwable}
If you had an instance of such a thing, you could of couse pattern match it. You could also invoke the takeAction method, supplying an instance of Motorbike and receiving a Throwable in response.
Now imagine it used with primitives, guaranteeing some basic arithmetic operations to be available regardless of exactly what type you had. It's an approach that would make a nice counterpoint to specialisation, and could make it easy to avoid a lot of boxing/unboxing performance issues depending on the underlying implementation.
For more info, there's a draft paper here http://lampwww.epfl.ch/teaching/foundations_of_software/docs/dependent_types.pdf
You can also google against the magic words "dependent object types" for other papers and to get a feel for how these ideas evolved.
Note that if the type signatures are conflicting, the current mixin axiom is to use the "last" definition in composition order (you may wanna check SLS or some other source before taking my word for it). In that sense, the current "with" statement is not strictly an intersection type, or rather, it's an intersection type with an additional paradigm.
Thanks a lot for the link, will look at it. I need more lives...Now imagine it used with primitives, guaranteeing some basic arithmetic operations to be available regardless of exactly what type you had. It's an approach that would make a nice counterpoint to specialisation, and could make it easy to avoid a lot of boxing/unboxing performance issues depending on the underlying implementation.
For more info, there's a draft paper here http://lampwww.epfl.ch/teaching/foundations_of_software/docs/dependent_types.pdf
You can also google against the magic words "dependent object types" for other papers and to get a feel for how these ideas evolved.
Regards,
Jan
On 7 March 2012 20:50, Jan Vanek <j3v...@googlemail.com> wrote:
On 07.03.2012 19:03, Tom Switzer wrote:allowed access to the members though, how is List[Int] V List[String] handled? What does .head return? Int V String? Or would it
Yes. Int V String. As Adriaan wrote:I don't know but wildly speculate that methods with parameters are synonymous only when they have identical parameter types. So if there is:
where the intersection is computed by including only synonymous members from both MS and MT,say val m: Ts from MS and val m: Tt from MT yield a member val m: Ts \/ Tt of the intersected set
class T { def f(i: Int, j: Int) {} }
class S { def f(s: String, t: String) {} }
in T v S there is no f. This would also mean that if there is var m: Ts and var m: Tt then in the T v S there is only def m: Ts V Tt (getter), but no setter.
Regards,
Jan
What would be the use case for this feature?
these encoding are neat, but the union types we're working on go beyond what you can do "in user space"assume membersOf(S) = MS, membersOf(T) = MT
then, membersOf(S \/ T) = MS intersect MT, (not a typo -- the members of a union type is the intersection of the sets of members of its constituents)
where the intersection is computed by including only synonymous members from both MS and MT,say val m: Ts from MS and val m: Tt from MT yield a member val m: Ts \/ Tt of the intersected set
conversely, membersOf(S /\ T) = MS union MT (where, again, overlapping members have their types intersected)(note that this is a work in progress, so I won't speculate on when/how/whether/... this ends up in a scalac near you)
On Tuesday, March 6, 2012 11:16:37 PM UTC+1, Rex Kerr wrote:
On Tue, Mar 6, 2012 at 4:52 PM, Shelby <she...@coolpage.com> wrote:If my (twist on Miles Sabin's) implementation above is correct,
apparently the disjunction is the conjunction with subtyping in the
contravariant direction, with each item in the conjunction subtyped in
the contravariant direction. That appears to be a statement of De
Morgan's laws where negation is subtyping in the contravariant
direction.
Yes, that's correct--I believe that I had an exchange with Miles regarding exactly that in the comments on his union types post.