Scala函数在列表中丢失变量类型?

时间:2015-08-28 06:44:38

标签: scala generics akka type-erasure

背景

我试图通过从Scala中的匿名函数动态创建/组合部分函数(case ...)来减少代码并改进Akka中的代码重用。

要创建这些部分功能,我需要访问功能'参数类型虽然(使用类型参数T),但不幸的是这取决于类型擦除。

我发现使用TypeTagTypeClass es我可以解决这个问题,这很棒。但是,我不是一次一个地将我的功能转换为部分功能,而是希望使用.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

对于Akka好奇,我的实际功能,而不是上面的check()

def caseFn[T](fn: T => Unit)(implicit ct: ClassTag[T]): Actor.Receive = {
  case ct(msg: T) => {
    fn(msg)
  }
}

3 个答案:

答案 0 :(得分:5)

您可以通过更改

来使其正常工作
val lst = List(fn).map(check)

val lst = List(fn).map(check(_))

这里发生了什么?

map(check)案例中,Scala称为eta-expansion将方法(check)转换为函数,请参阅Scala Language Specification Version 2.9的6.26.5:

  

6.26.5 Eta扩展

     

Eta-expansion将方法类型的表达式转换为函数类型的等效表达式。它分两步进行。首先,一个标识 e 的最大子表达式;让我们说这些是 e_1 ,..., e_m 。对于其中的每一个,都会创建一个新名称​​ x_i 。让 e' 成为将 e 中的每个最大子表达式 e_i 替换为相应的新名称​​ x_i 。其次,为方法的每个参数类型 T_i 创建一个新名称​​ y_i (i = 1,... n)。那么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-expansion ,并且必须推断泛型方法check的类型(在eta扩展期间生成)。由于Scala中类型推断的限制,它将推断Nothing而不是String,因此第一个版本不起作用,而第二个版本不起作用。

答案 1 :(得分:1)

如果你这样做会更好:

val lst = List(fn).map(check(_))
// lst: List[scala.reflect.ClassTag[String]] = List(java.lang.String)

不完全确定原因。

答案 2 :(得分:1)

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)