0
votes
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.

enter image description here

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 ?

1

1 Answers

1
votes

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).