如何专注于 Scala 中的类型投影? [英] How to specialize on a type projection in Scala?

查看:37
本文介绍了如何专注于 Scala 中的类型投影?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题陈述

考虑一个包含抽象类型成员A的类型T:

Consider a type T that contains an abstract type member A:

trait T {
  type A
}

我想创建一个将 T0 <: T 作为类型参数的类,但专门研究 类型投影 T0#A.例如,下面的方法 foo 可以特化吗?

I'd like to create a class that takes a T0 <: T as a type parameter, but specializes on the type projection T0#A. For example, in the following, can the method foo be specialized?

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

请注意,使用 @specialized 注释 T0 不会达到预期的结果.是否有专门针对类型投影 T#Afoo 的好方法?

Note that annotating T0 with @specialized will not achieve the desired result. Is there is a good way to specialize foo on the type projection T#A?

有限的解决方案:从带有额外参数的特殊父类继承

在这种特殊情况下,这里有一种专门处理 T0#A 的方法:

In this particular case, here's a way to specialize on 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 是专门的.

By inheriting from the specialized parent class SpecializedFoo, we ensure that Foo2.foo is specialized.

专业化验证

为了验证 Foo2.foo 而不是 Foo.foo 是特化的,我们可以使用显式的 T 调用它们,其中 T#A 是一个原始的 Double,

To verify that Foo2.foo, but not Foo.foo, is specialized, we can call them with an explicit T where T#A is a primitive 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)
}

可以使用命令:javap -v Test"从 REPL 检查字节码,

The bytecode can be examined from the REPL with the command ":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 中.

Note that boxing appears in test1 but not test2.

限制

Edit 7/9 上面的技巧比我最初意识到的要有限.专门用于这种情况,它根本不起作用:

Edit 7/9 The trick above is more limited than I realized at first. It won't work at all for specializing this case:

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 的类型参数,但我想知道是否可以避免这种情况.

I see no reason why a (hypothetical) compiler couldn't specialize on A in principle; a usual, the specialized versions would only be usable when a specific T#A is known at compile time. The natural practical solution is to lift A into a type parameter of T, but I was wondering if I could avoid that.

推荐答案

这是编译器限制;一个不能通常专注于类型参数的元素.但是,建议的技巧足以满足我的目的:

This is a compiler limitation; one cannot generally specialize on elements of a type parameter. However, the proposed trick is good enough for my purposes:

trait Types {
  type A
  type B
}

trait GenOps[@specialized A, @specialized B] {
  ...
}

trait Ops[T <: Types] extends GenOps[T#A, T#B]

通过这种方式,特征 Ops 变得特殊,因为它继承了特征 GenOps 中的特殊实现.我的动机是我希望 trait Ops 采用单个类型参数 T,而不是 T#AT#B(当 Ops 也采用期望 T 作为参数的更高种类类型时,这变得必要).

This way the trait Ops gets specialized because it inherits the specialized implementations in trait GenOps. My motivation is that I want trait Ops to take a single type parameter T, rather than both T#A and T#B (this becomes necessary when Ops also takes a higher kinded type that expects T as a parameter).

这篇关于如何专注于 Scala 中的类型投影?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆