Scala函数在列表中丢失变量类型?
背景: 我试图通过从Scala中的匿名函数动态创建/组合部分函数(case…)来减少代码并提高Akka中的代码重用 要创建这些部分函数,我需要访问函数的参数类型(使用类型参数T),但不幸的是,这会被类型擦除 我发现使用Scala函数在列表中丢失变量类型?,scala,generics,akka,type-erasure,Scala,Generics,Akka,Type Erasure,背景: 我试图通过从Scala中的匿名函数动态创建/组合部分函数(case…)来减少代码并提高Akka中的代码重用 要创建这些部分函数,我需要访问函数的参数类型(使用类型参数T),但不幸的是,这会被类型擦除 我发现使用TypeTags或TypeClasses我可以处理这个问题,这很好。但是,我希望使用.map()成批地将函数转换为部分函数,而不是一次转换一个函数 然而,这似乎是失败的;当通过映射使用函数时,它似乎突然变为Nothing,使我的函数功能失调(并非有意双关语) TL;DR:我可以让l
TypeTag
s或TypeClass
es我可以处理这个问题,这很好。但是,我希望使用.map()
成批地将函数转换为部分函数,而不是一次转换一个函数
然而,这似乎是失败的;当通过映射使用函数时,它似乎突然变为Nothing
,使我的函数功能失调(并非有意双关语)
TL;DR:我可以让lst(0)也给出字符串吗
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
对于Akka好奇者来说,我的实际函数是有问题的,而不是上面的check()
:
如果您这样做,效果会更好:
val lst = List(fn).map(check(_))
// lst: List[scala.reflect.ClassTag[String]] = List(java.lang.String)
不完全确定原因。scala>检查_
scala> check _
res19: (Nothing => Unit) => scala.reflect.ClassTag[Nothing] = <function1>
scala> check(_: String => Unit)
res20: (String => Unit) => scala.reflect.ClassTag[String] = <function1>
scala> List(fn).map(check)
res21: List[scala.reflect.ClassTag[Nothing]] = List(Nothing)
scala> List(fn).map(check _)
res22: List[scala.reflect.ClassTag[Nothing]] = List(Nothing)
scala> List(fn).map(check(_))
res23: List[scala.reflect.ClassTag[String]] = List(java.lang.String)
res19:(Nothing=>Unit)=>scala.reflect.ClassTag[Nothing]=
scala>check(ux:String=>Unit)
res20:(String=>Unit)=>scala.reflect.ClassTag[String]=
scala>列表(fn).map(检查)
res21:List[scala.reflect.ClassTag[Nothing]]=List(Nothing)
scala>列表(fn).map(选中)
res22:List[scala.reflect.ClassTag[Nothing]]=List(Nothing)
scala>列表(fn).map(勾选(33;))
res23:List[scala.reflect.ClassTag[String]]=List(java.lang.String)
您可以通过更改
val lst = List(fn).map(check)
到
这是怎么回事
在map(check)
情况下,Scala进行所谓的eta扩展
,以将方法(check
)转换为函数,请参见第6.26.5节:
6.26.5 Eta扩展
Eta扩展将方法类型的表达式转换为函数类型的等效表达式。它分两步进行。首先,确定e的最大子表达式;假设这些是e_1,…,e_m。对于其中的每一个,我们都会创建一个新名称x_i。设e'是将e中的每个最大子表达式e_i替换为相应的新名称x_i而得到的表达式。其次,为方法的每个参数类型T_i(i=1,…n)创建一个新名称y_i。eta转换的结果是:
{ 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扩展,并且必须推断通用方法check
的类型(在eta扩展期间生成)。由于Scala中类型推断的局限性,它将推断无
而不是字符串
,因此第一个版本不起作用,而第二个版本不起作用。>不完全确定原因。=>eta扩展:),更多信息请参见我的答案
val lst = List(fn).map(check(_))
{ val x_1 = e_1;
...
val x_m = em;
(y_1: T_1,...,y_n:T_n) => e'(y_1,...,y_n)
}