【问题标题】:scala - higher order functions change type T to type Nothingscala - 高阶函数将类型 T 更改为类型 Nothing
【发布时间】:2019-06-30 23:37:30
【问题描述】:

疑难解答环境:sbt 控制台 (Scala 2.11.8) & spark-shell (Spark 2.3, Scala 2.11)

我有一个视图绑定类型为 T... 的高阶函数,但是当函数被部分应用时,arg t: T 类型签名从 T <% Double 变为 Nothing

演示的玩具示例:

// tot: T needs to work on (at least) Int, Long, Double, Float
// no common supertype -> some kind of context bound

def func[T <% Double](isValid: Boolean)(tot: T, cnt: Int): Double = 
  if (isValid) tot.toDouble / cnt else Double.NaN

当我尝试部分应用 isValid 时,我希望结果为 (T, Int) =&gt; Double 类型,但类型最终为 (Nothing, Int) =&gt; Double,我无法传入 arg tot

val f1 = func(true)_   // f1: (Nothing, Int) => Double = <function2>
val f2 = func(false)_  // f2: (Nothing, Int) => Double = <function2>

val g1 = f1(10.0, 1)
// <console>:40: error: type mismatch;
// found   : Double(10.0)
// required: Nothing
//       val g1 = f1(10.0, 1)

我在定义 f1 或 f2 时没有收到任何错误消息...所以很难解释。它只是将 arg tot: T 转换为类型 Nothing

检查 scala 文档...我看到 scala.Nothing 是所有其他类型的子类型,所以我认为它可能正在丢失 T 上的视图绑定...这可能与类型擦除有关...所以我尝试了使用 ClassTag...

import scala.reflect.ClassTag

def func[T <% Double](isValid: Boolean)(tot: T, cnt: Int)(implicit tag: ClassTag[T]): Double = 
  if (isValid) tot.toDouble / cnt else Double.NaN

这没有帮助。同样的问题。

如果我尝试使用implicit num: Numeric[T],它会以一种新的方式阻塞Nothing 类型...

def func[T](isValid: Boolean)(tot: T, cnt: Int)( implicit num: Numeric[T] ): Double = 
  if (isValid) num.toDouble(tot) / cnt else Double.NaN

val f1 = func(true)_
// <console>:40: error: could not find implicit value for parameter num: Numeric[Nothing]
//        val f1 = func(true)_

如果我一次全部应用它(在顶部使用第一个 'func'),它可以正常工作...

val g1 = func(true)(10.0, 1)
// g1: Double = 10.0

但在我的真实(非玩具)代码中,这不是一个选项。

这里发生了什么,我怎样才能使 func 在部分应用时工作?

编辑 [@Alexey 的解决方案]

我无法使用首选的“def”方法。

def func[T <% Double](isValid: Boolean)(tot: T, cnt: Int): Double =
  if (isValid) tot.toDouble / cnt else Double.NaN
// func: [T](isValid: Boolean)(tot: T, cnt: Int)(implicit evidence$1: T => Double)Double

def f1[T <% Double]: ((T, Int) => Double) = func[T](true)_
// f1: [T](implicit evidence$1: T => Double)(T, Int) => Double

f1[Double](10.0, 1)
<console>:41: error: too many arguments for method f1: (implicit evidence$1: Double => Double)(Double, Int) => Double
   f1[Double](10.0, 1)

【问题讨论】:

    标签: scala types polymorphism higher-order-functions


    【解决方案1】:

    当我尝试部分应用 isValid 时,我希望结果是类型 (T, Int) =&gt; Double

    值不能是通用的。因此,对于某些特定的T,它可以具有这种类型,但是您没有提供允许推断它的确切参数。您可以指定例如

    val f1 = func[TheTypeYouWant](true) _
    

    val f1: (TheTypeYouWant, Int) => Double = func(true) _
    

    如果你希望它是通用的,它必须再次是 def

    def f1[T <% Double] = func[T](true) _
    

    【讨论】:

    • 谢谢阿列克谢!当我尝试您的解决方案时,使用def f1 ... 类型签名最终为[T](implicit evidence$1: T =&gt; Double)(T, Int) =&gt; Double。我正在阅读这里试图找出作为“隐含证据”参数传递的内容:scala-lang.org/files/archive/spec/2.11/…
    • @kmh you 不必传递任何隐含证据,您只需指定类型 TT 可隐式转换为 Double 的隐含证据将由编译器自动提供。这就是隐式参数的全部意义,不是吗?
    • 谢谢安德烈!当我尝试“def”方法时,f1 以类型签名 f1: [T](implicit evidence$1: T =&gt; Double)(T, Int) =&gt; Double 结束,这似乎没问题,但是当我调用 f1[Double](10.0, 1) 时,它会因“方法 f1 的参数太多”而窒息,我无法解决。 (原始帖子底部的“编辑”完全错误)
    • @kmh 试试f1[Double].apply(10.0, 1)
    • @kmh 正如布赖恩所说。或者您可以将其存储在 val 中:val g1 = f1[Double]; g1(10.0, 1).
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-02
    • 2012-10-25
    • 1970-01-01
    • 1970-01-01
    • 2017-01-01
    • 1970-01-01
    相关资源
    最近更新 更多