Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In scala, how to instruct the compiler to realise the equivalence of two abstract types?

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?

like image 703
tribbloid Avatar asked Jun 19 '20 20:06

tribbloid


People also ask

Which class is used to achieve abstraction in Scala?

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.

How to compile source code files to the classes directory in Scala?

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:

Why Scala is better than other programming languages?

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.

Can we implement methods in traits in Scala?

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


1 Answers

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]
like image 164
Willis Blackburn Avatar answered Dec 22 '22 00:12

Willis Blackburn