Как специализироваться на типе проекция в Scala?

Постановка вопроса

Рассмотрим тип T , который содержит член абстрактного типа A :

trait T {
  type A
}

Я хотел бы создать класс, который принимает T0 <: T в качестве параметра типа, но специализируется на проекции типа T0 # A . Например, может ли метод foo быть специализированным ниже?

class Foo[T0 <: T] {
  def foo(a: T0#A, f: T0#A => T0#A) = f(a)
}

Обратите внимание, что аннотирование T0 с помощью @specialized не приведет к желаемому результату. Есть ли хороший способ специализации foo на проекции типа T # A ?

Ограниченное решение: наследование от специализированного родительского класса с дополнительным параметром

В данном конкретном случае случае, вот способ специализации на T0 # A :

trait SpecializedFoo[@specialized A0, T0 <: T] {
  def foo(a: A0, f: A0 => A0) = f(a)
}
class Foo2[T0 <: T] extends SpecializedFoo[T0#A, T0]

Наследуя специализированный родительский класс SpecializedFoo , мы гарантируем, что Foo2.foo специализирован .

Проверка специализации

Чтобы проверить, что Foo2.foo , но не Foo.foo , является специализированным, мы можем вызвать их с явным T , где T # A - примитивный тип Double,

trait ExplicitT extends T {
  type A = Double
}

object Test {
  def test1 = (new Foo[ExplicitT]).foo(1.0, _ + 1.0)
  def test2 = (new Foo2[ExplicitT]).foo(1.0, _ + 1.0)
}

Байт-код можно проверить из REPL с помощью команды «: javap -v Test»,

public double test1();
  Code:
   Stack=4, Locals=1, Args_size=1
   0:   new #16; //class Foo
   3:   dup
   4:   invokespecial   #18; //Method Foo."<init>":()V
   7:   dconst_1
   8:   invokestatic    #24; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
   11:  new #26; //class Test$$anonfun$test1$1
   14:  dup
   15:  invokespecial   #27; //Method Test$$anonfun$test1$1."<init>":()V
   18:  invokevirtual   #31; //Method Foo.foo:(Ljava/lang/Object;Lscala/Function1;)Ljava/lang/Object;
   21:  invokestatic    #35; //Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
   24:  dreturn
  LineNumberTable: 
   line 13: 0


public double test2();
  Code:
   Stack=5, Locals=1, Args_size=1
   0:   new #38; //class Foo2
   3:   dup
   4:   invokespecial   #39; //Method Foo2."<init>":()V
   7:   dconst_1
   8:   new #41; //class Test$$anonfun$test2$1
   11:  dup
   12:  invokespecial   #42; //Method Test$$anonfun$test2$1."<init>":()V
   15:  invokeinterface #48,  4; //InterfaceMethod SpecializedFoo.foo$mcD$sp:(DLscala/Function1;)D
   20:  dreturn
  LineNumberTable: 
   line 14: 0

Обратите внимание, что бокс появляется в test1 , но не test2 .

Ограничения

Изменить 7/9 Приведенный выше трюк более ограничен, чем я думал вначале. Это вообще не сработает для специализации этого случая:

trait T {
  type A
  def x: A
  def f: A => Double
}

class Foo[T0 <: T] {
  def foo(t: T0) = t.f(t.x)
}

Я не вижу причин, по которым (гипотетический) компилятор не мог бы специализироваться на A в принципе ; Как правило, специализированные версии можно будет использовать только тогда, когда во время компиляции известен конкретный T # A . Естественным практическим решением является преобразование A в параметр типа T , но мне было интересно, могу ли я этого избежать.

10
задан Kipton Barros 9 July 2011 в 17:51
поделиться