一流功能中的部分应用功能

时间:2018-06-22 11:53:20

标签: scala

  val divide = (num: Double, den: Double) => {
  num / den
  }

  //type of pafv1 is () => (Double,Double) => Double
  val pafv1 = divide _


  //type of pafv2 is Double => Double
  val pafv2 = divide(_:Double,2)

为什么pafv1的类型不是(Double,Double) => Double? 当divide是一种简单方法时,pafv1的类型为(Double,Double) => Double

4 个答案:

答案 0 :(得分:2)

我认为解释来自eta expansion

Scala生成val的getter和setter方法,这意味着divide实际上是方法def divide = (num: Double, den: Double) => { num / den }

执行val pafv1 = divide _时,会将其扩展为eta扩展除法版本,即() => (num: Double, den: Double) => { num / den }

为什么要进行eta扩展?因为您尝试使用方法作为值(您正在部分评估_,因此将结果强制为函数值)。

答案 1 :(得分:1)

这很奇怪...如果您编译这段代码:

class C {
  val f = (n: Double, d: Double) => n / d
  val pf = f _
}

带有-print标志,已解密的代码为:

package <empty> {
  class C extends Object {
    private[this] val f: Function2 = _;
    <stable> <accessor> def f(): Function2 = C.this.f;
    private[this] val pf: Function0 = _;
    <stable> <accessor> def pf(): Function0 = C.this.pf;
    final <artifact> private[this] def $anonfun$f$1(n: Double, d: Double): Double = n./(d);
    final <artifact> private[this] def $anonfun$pf$1(): Function2 = C.this.f();
    def <init>(): C = {
      C.super.<init>();
      C.this.f = {
        ((n: Double, d: Double) => C.this.$anonfun$f$1(n, d))
      };
      C.this.pf = {
        (() => C.this.$anonfun$pf$1())
      };
      ()
    }
  }
}

一些观察:

  • val f声明分为private this val f: Function2<stable> <accessor> def f(): Function2
  • 稳定的访问器方法包装在匿名函数$anonfun$pf$1()中,该函数将所有调用重定向到C.this.f()
  • pf eta展开无参数的$anonfun$pf$1()方法到一个不带参数并返回Function2的函数中。

如果我不得不写下一段等价的代码来演示“实际发生的事情”,那么我可能会写这样的东西:

class C2 {
  var f: Function2[Double, Double, Double] = _
  def fAccessor(): Function2[Double, Double, Double] = this.f

  f = (n: Double, d: Double) => n / d
  val pf = fAccessor _
}

在这里您看到:

  • fAccessor是不带参数的方法,该方法返回Function2
  • pf是访问者方法fAccessor的eta扩展,它也接受零个参数,并返回一个Function2

因此,可以得出结论:由于某些奇怪的原因,f _是其他不可见的访问器方法的eta扩展。我认为这根本不应该编译,看起来像是抽象泄漏(一些依赖于综合实现的方法符号出现在任何地方)。


您可能想要的东西:

val pf: Double => Double => Double = n => f(n, _)

答案 2 :(得分:0)

我通常用case表达式“包装”该函数:

val divide = (num: Double, den: Double) => num / den

val partialDivide: PartialFunction[(Double, Double), Double] = {
  case (num: Double, den: Double) if den != 0 => divide(num, den)
}

partialDivide(4, 2)
partialDivide(4, 0) // throws a `MatchError`

您可以使用此代码on Scastie

您可以找到关于主题here的详尽解释。

答案 3 :(得分:0)

scala language specification可以看到:

如果e是无参数方法…,则e _表示类型()=> T的函数,当将e应用于空参数列表()时,该函数会求值e。

这就是我们所看到的。