【问题标题】:Scala functions lose variable type in list?Scala函数在列表中丢失变量类型?
【发布时间】:2015-08-28 06:44:37
【问题描述】:

背景:

我正在尝试通过从 Scala 中的匿名函数动态创建/组合部分函数(案例...)来减少代码并改进 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

对于Akka-curious,我的实际功能有问题,而不是上面的check()

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

【问题讨论】:

    标签: scala generics akka type-erasure


    【解决方案1】:

    你可以通过改变来让它工作

    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-conversion的结果是:

    { 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-expansion 期间生成)。由于Scala中类型推断的限制,它会推断Nothing而不是String,因此第一个版本不起作用,而第二个版本起作用。

    【讨论】:

      【解决方案2】:

      如果你这样做,效果会更好:

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

      不完全确定原因。

      【讨论】:

      • > 不完全确定原因。 => eta-expansion :),请参阅我的答案了解更多
      【解决方案3】:
      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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-22
        • 1970-01-01
        • 2010-12-22
        • 1970-01-01
        • 2013-11-17
        • 1970-01-01
        • 2018-05-03
        相关资源
        最近更新 更多