Reputation: 20653
object ExistentialTypesAsFunctionParameters extends App{
def m(p: T forSome {type T})=p.toString
def m1(p:Any)=p.toString
println(m("Hello"))
println(m1("Hello"))
}
This program runs and prints Hello
twice.
What is the difference between m
and m1
, is there any ?
What are the values that are allowed by the existential type T forSome {type T}
as compared to Any
?
Most importantly:
How can these questions be answered based on the Scala Language Specification ?
In other words, an explanation is needed in terms of the Scala Language Specification.
EDIT:
Follow up question:
class Top
class Middle extends Top {override def toString()="Middle"}
class Bottom extends Middle
object ExistentialTypesAsFunctionParameters extends App{
def m3(p: T forSome {type T >: Middle })=p.toString
println(m3(new Middle))
println(m3(new Bottom))
println(m3(new Top))
}
This code compiles and runs.
But I would expect println(m3(new Bottom))
not to compile because of the restriction T forSome {type T >: Middle }
that should mean that any value v
whose type can be a subtype of Middle
should not have the type T forSome {type T >: Middle }
.
So why does this code still compile ?
Upvotes: 0
Views: 98
Reputation: 17431
The type of the expression p
in p.toString
is the skolemization of T forSome {type T}
, per SLS section 6.1:
The following skolemization rule is applied universally for every expression: If the type of an expression would be an existential type T , then the type of the expression is assumed instead to be a skolemization (§3.2.10) of T .
which we can see is Any
by following the skolemization rules in your quote.
So when used as the type of an expression, T forSome {type T}
has the same effect as Any
, and it will admit the same possible values. And when used as a type, it will be considered to conform to a given type iff its skolemization does (per 3.5.2). But the types are not equivalent as defined in section 3.5.1. That said, I can't see any way to distinguish between them even at the type level.
Edit: T forSome {type T >: Middle}
is just any supertype of Middle
, i.e. again Any
conforms to this. Any >: Middle
so Any <: T forSome {type T >: Middle}
, and Bottom <: Any
, so Bottom
can still be passed. If you want to prevent subtypes of a particular type being passed you can use a typeclass that doesn't exists for those subtypes (by deliberately creating an implicit resolution conflict for those subtypes).
Upvotes: 1