Tyc*_*cho 3 generics scala type-erasure akka
背景:
我试图通过动态创建/组合Scala中的匿名函数的部分函数(case ...)来减少代码并改进Akka中的代码重用.
要创建这些部分函数,我需要访问函数的参数类型(使用类型参数T),但遗憾的是这受到类型擦除的影响.
我发现使用TypeTags或TypeClasses我可以处理它,这很好.但是,我不是一次一个地将我的功能转换为部分功能,而是希望批量使用.map().
然而,这似乎是失败的; 当通过地图使用该功能时,似乎突然变成T Nothing,导致我的功能功能失常(没有双关语意).
TL; DR:我可以得到lst(0)给String吗?
import scala.reflect.ClassTag
def fn = (s: String) => {}
def check[T](fn: T => Unit)(implicit ct: ClassTag[T]) = ct
check(fn)
//scala.reflect.ClassTag[String] = java.lang.String
val lst = List(fn).map(check)
lst(0)
//scala.reflect.ClassTag[Nothing] = Nothing
Run Code Online (Sandbox Code Playgroud)
对于Akka好奇,我的实际功能,而不是上述check():
def caseFn[T](fn: T => Unit)(implicit ct: ClassTag[T]): Actor.Receive = {
case ct(msg: T) => {
fn(msg)
}
}
Run Code Online (Sandbox Code Playgroud)
你可以通过改变来使它工作
val lst = List(fn).map(check)
Run Code Online (Sandbox Code Playgroud)
至
val lst = List(fn).map(check(_))
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?
在这种map(check)情况下,Scala eta-expansion会将method(check)转换为函数,请参阅Scala语言规范版本2.9的 6.26.5 :
6.26.5 Eta扩展
Eta-expansion将方法类型的表达式转换为函数类型的等效表达式.它分两步进行.首先,一个标识e的最大子表达式; 让我们说这些是e_1,...,e_m.对于其中的每一个,都会创建一个新名称x_i.让E"是从替换每个最大子表达式产生的表达e_i在ë由相应的新鲜名称X_I.其次,为方法的每个参数类型T_i创建一个新名称y_i(i = 1,... n).eta转换的结果是:
Run Code Online (Sandbox Code Playgroud){ val x_1 = e_1; ... val x_m = em; (y_1: T_1,...,y_n:T_n) => e'(y_1,...,y_n) }
因此,在map(check)Scala执行eta扩展并且必须推断泛型方法的类型(在eta扩展期间生成)check.由于Scala中类型推断的限制,它将推断Nothing而不是String,因此第一个版本不起作用,而第二个版本不起作用.
| 归档时间: |
|
| 查看次数: |
152 次 |
| 最近记录: |