Search code examples
scalagenericstype-members

Why is A => String not the same as _1.type => String where val _1: A?


In this Scala 2.13 example:

import scala.language.implicitConversions

object Main2 extends App {

  trait Foo {
    type F = this.type => String
  }

  def fooA[A <: Foo](f: A#F, toString: A#F => String): String = toString(f)
  def fooB[A <: Foo](f: A#F, toString: (A => String) => String): String = toString(f)
}

It fails with:

type mismatch;
 found   : _1.type => String where val _1: A
 required: A => String
  def fooB[A <: Foo](f: A#F, toString: (A => String) => String): String = toString(f)

Why is A => String not the same as _1.type => String where val _1: A?

Is it safe to cast f.asInstanceOf[(A => String)]?

Is there a concrete example where this doesn't work?


Solution

  • I think you have misunderstanding what is this.type. From scala 2.13 spec:

    Every value in Scala has a type which is of one of the following forms. A singleton type is of the form p . p.type. Where p p is a path pointing to a value which conforms to scala.AnyRef, the type denotes the set of values consisting of null and the value denoted by p p (i.e., the value v v for which v eq p)

    It means, that this.type will be uniq for each instance of Foo. In fooB:

    def fooB[A <: Foo](f: A#F, toString: (A => String) => String): String = toString(f)
    

    compiler sees that you tries to pass f which type is:

    (some object of A which upper-bouned by Foo).type => String
    

    function from singleton-type object to String into function what takes function from object of type A into String.

    Note object of type A (new A()) has not the same type as (new A()).type, they have different types and this is the reason why compiler shows you an error.

    for example, you can write something like that and it will compiles:

    class Bar extends Foo {
      def conversion: this.type => String = b => "bar"
    }
    def someStringConversion[A <: Foo]: A#F => String = ???
    val bar: Bar = new Bar
    someStringConversion[Bar](bar.conversion)
    val conversionType: bar.type => String = bar.conversion
    val customConversion: bar.type => String = (x: bar.type) => "some string"
    someStringConversion[Bar](customConversion)
    

    because of bar.type has the same type as this.type (they are both refer into one singleton type - bar.type

    Conclusion

    Your functions fooA and fooB have different signatures, A#F => String is not the same as A => String and you can't use asInstanceOf on first. fooB will not work on any instance of Foo because any Foo instance has it's own this.type singleton type. It doesn't relate to Foo like some Foo implementation relates to Foo.