I have a simple case to test the type inference capability of scala:
trait Super1[S] {
final type Out = this.type
final val out: Out = this
}
trait Super2[S] extends Super1[S] {
final type SS = S
}
case class A[S](k: S) extends Super2[S] {}
val a = A("abc")
implicitly[a.type =:= a.out.type]
// success
implicitly[a.Out =:= a.out.Out]
// success
implicitly[a.SS =:= a.out.SS]
implicitly[a.SS <:< a.out.SS]
implicitly[a.out.SS <:< a.SS]
// oops
The error for the last 4 lines looks like this:
[Error] /home/peng/git-spike/scalaspike/common/src/test/scala/com/tribbloids/spike/scala_spike/AbstractType/InferInheritance.scala:28: Cannot prove that a.SS =:= Super2.this.SS.
[Error] /home/peng/git-spike/scalaspike/common/src/test/scala/com/tribbloids/spike/scala_spike/AbstractType/InferInheritance.scala:29: Cannot prove that a.SS <:< Super2.this.SS.
[Error] /home/peng/git-spike/scalaspike/common/src/test/scala/com/tribbloids/spike/scala_spike/AbstractType/InferInheritance.scala:30: Cannot prove that Super2.this.SS <:< a.SS.
three errors found
Clearly scala compiler screwed up on these cases: if I move:
final type Out = this.type
final val out: Out = this
to be under Super2
it will compile successfully. My question is that: why the status quo inference algorithm won't work in this case? and how can I rewrite my code to circumvent this problem of the compiler?
Abstract class is used to achieve abstraction. Abstraction is a process in which we hide complex implementation details and show only functionality to the user. In scala, we can achieve abstraction by using abstract class and trait.
Given this configuration, to compile your source code files to the classes directory, use the following command: Assuming Main.scala is an object that extends App, Pizza.scala is a regular class file, and Topping.scala is a case class, your classes directory will contain these files after your scalac command:
Unlike other statically typed programming languages like C++, C etc., Scala doesn’t expect the redundant type of information from the user. In most cases, the user has no need to specify a type. Extensible: New language constructs can be added to Scala in form of libraries.
} In Scala, we are allowed to implement the method (only abstract methods) in traits. If a trait contains method implementation, then the class which extends this trait need not implement the method which already implemented in a trait. As shown in the below example. Traits does not contain constructor parameters.
If a.SS =:= a.out.SS
then we should be able to use one in place of the other. Is that the case?
No.
val x: a.SS = "hello"
val y: a.out.SS = "hello"
Compiling this I get:
ScalaFiddle.scala:29: error: type mismatch;
found : lang.this.String("hello")
required: Super2.this.SS
(which expands to) S
val y: a.out.SS = "hello"
It looks like Scala understands that a.SS
is really String
, which isn't surprising.
But clearly a.out.SS
isn't String
but rather... S
.
Strangely this works even though it's clearly wrong:
val b = A(1)
implicitly[a.out.SS =:= b.out.SS]
If you just define out
to be of type this.type
then your code works.
The best I can come up with is that when defining the type of out
, Scala fails to associate the actual type represented by S
with out
and so the type SS
as viewed through out
just has the generic type S
.
The type Out
does seem to understand the actual type represented by S
, so this works:
implicitly[a.SS =:= a.Out#SS]
implicitly[a.SS <:< a.Out#SS]
implicitly[a.Out#SS <:< a.SS]
and this correctly fails to compile:
val b = A(1)
implicitly[a.Out#SS =:= b.Out#SS]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With