【问题标题】:In Scala, can generic type parameters be used with *function* definitions?在 Scala 中,泛型类型参数可以与 *function* 定义一起使用吗?
【发布时间】:2013-03-07 06:47:39
【问题描述】:

是否有允许函数字面量上的泛型类型参数的语法?我知道我可以把它包装成这样的方法:

def createLongStringFunction[T](): (T) => Boolean = {
  (obj: T) => obj.toString.length > 7
}

但是我最终需要为每个类型 T 调用该方法并获得一个新函数。我查看了语言参考,虽然我看到编译器将函数文字语法转换为本身具有通用输入类型的 Functionn 对象的实例,但看起来编译器魔法实现了这些创建时的参数。实际上,我还没有找到任何允许我“使 Functionn 的一个或多个类型参数未绑定”的语法。我更喜欢这样的东西:

// doesn't compile
val longStringFunction: [T](T) => Boolean = (obj: T) => obj.toString.length > 7

这样的事情存在吗?或者就此而言,当被扩展的方法具有泛型参数时,eta-expansion 函数的显式类型是什么?

这是一个纯属人为且无用的示例。当然,我可以让函数在这里使用 Any。

【问题讨论】:

    标签: scala generics


    【解决方案1】:

    不,类型参数仅适用于方法而不适用于函数对象。例如,

    def f[T](x: T) = x     //> f: [T](x: T)T
    val g = f _            //> g: Nothing => Nothing = <function1>
    // g(2)                // error
    val h: Int=>Int = f _  //> h  : Int => Int = <function2>
    h(2)                   //> res0: Int = 2
    

    方法f无法转换为多态函数对象g。可以看到,g的推断类型其实是Function1[Nothing, Nothing],没用。但是,通过类型提示,我们可以构造 h: Function1[Int,Int],它对 Int 参数按预期工作。

    【讨论】:

    • 谢谢,我担心可能是这样。不过,以这种方式扩展该方法确实为更复杂的情况打开了大门。虽然我找不到没有中间方法的方法,但赋值上的类型提示可以使这一点出人意料地远,例如:def doStuff[T, U](moreStuff: T =&gt; U)(obj: T) = moreStuff(obj) val timeAndAHalf = doStuff[Int, Double](_ * 1.5) _ 与它的显式对应物def doStuff[T, U](moreStuff: T =&gt; U)(obj: T): U = moreStuff(obj) val timeAndAHalf: Int =&gt; Double = (num: Int) =&gt; doStuff[Int, Double](num =&gt; num * 1.5)(num) 相去甚远
    • 类型参数不适用于函数对象的原因是什么?
    • @PaulCarey 看看这个milessabin.com/blog/2012/04/27/…
    【解决方案2】:

    正如您所说,在您的示例中,您所需要的只是 toString 方法,因此 Any 将是通常的解决方案。但是,在将类型构造函数(例如 List)应用于元组中的每个元素等情况下,需要能够使用更高级别的类型。

    正如其他答案所提到的,对此没有直接支持,但有一种相对较好的编码方式:

    trait ~>[A[_],B[_]] {
      def apply[X](a : A[X]) : B[X]
    }
    
    type Id[A] = A //necessary hack
    
    object newList extends (Id ~> List) {
      def apply[X](a : Id[X]) = List(a)
    }
    
    def tupleize[A,B, F[_]](f : Id ~> F, a : A, b : B) = (f(a), f(b))
    
    tupleize(newList, 1, "Hello") // (List(1), List(Hello))
    

    【讨论】:

      【解决方案3】:

      由于longStringFunction 定义如下,是一个,它必须有一些给定类型

      val longStringFunction: (T) => Boolean = (obj: T) => obj.toString.length > 7
      

      但是,您可以通过方法重用函数对象:

      scala> val funObj: Any => Boolean = _.toString.size > 7
      funObj: Any => Boolean = <function1>
      
      scala> def typedFunction[T]: T => Boolean = funObj
      typedFunction: [T]=> T => Boolean
      
      scala> val f1 = typedFunction[String]
      f1: String => Boolean = <function1>
      
      scala> val f2 = typedFunction[Int]
      f2: Int => Boolean = <function1>
      
      scala> f1 eq f2
      res0: Boolean = true
      

      这是因为trait Function1[-T1, +R]T1 类型的逆变

      【讨论】:

      • 是的。这实际上只是转换为更受约束的类型。事实上,我们可以完全放弃该方法并简单地分配函数,例如val f1: String =&gt; Boolean = funObj
      【解决方案4】:

      在scala中,函数值是参数单态的(而方法是多态的)

      Shapeless 库引入了多态函数值,可以映射到 HList 和许多其他特性。

      请考虑以下参考: http://www.chuusai.com/2012/04/27/shapeless-polymorphic-function-values-1/ http://www.chuusai.com/2012/05/10/shapeless-polymorphic-function-values-2/

      【讨论】:

      • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
      • @Sasam 这个答案确实包含了答案的基本部分!
      猜你喜欢
      • 2020-06-13
      • 1970-01-01
      • 2015-03-09
      • 1970-01-01
      • 1970-01-01
      • 2011-10-19
      • 1970-01-01
      • 2021-08-09
      • 1970-01-01
      相关资源
      最近更新 更多