是否有可能强制对curry函数进行早期评估?

时间:2017-02-21 14:37:58

标签: scala currying

考虑一个返回另一个函数的函数:

def prepareFunction(args: List[Any]): String => Unit = {
  println(s"Slow processing of $args...")
  val results = args.map(a => s"processed $a")

  def doSomething(s: String): Unit = {
    println(s"Do something quick with $s and $results")
  }

  doSomething
}

这里的想法是:外部函数执行一些繁重的处理并返回一个内部函数,该函数使用在封闭范围中定义的变量:

val doSomethingWithArgs = prepareFunction(List("arg1", "arg2", 3)) 
   //> Slow processing of List(arg1, arg2, 3)...
doSomethingWithArgs("abc")
   //> Do something quick with abc and List(processed arg1, processed arg2, processed 3)
doSomethingWithArgs("cde") 
   //> Do something quick with cde and List(processed arg1, processed arg2, processed 3)

请注意,外部函数仅评估一次。

使用多个参数列表和Scala的Currying syntax,我们可以编写类似的内容:

def prepareCurried(args: List[Any])(s: String): Unit = {
  println(s"Slow processing of $args")
  val results = args.map(a => s"processed $a")

  def doSomething(s: String): Unit = {
    println(s"Do something quick with $s and $results")
  }

  doSomething(s)
}  

但每次都会评估“外部”功能:

val doSomethingWithOtherArgs = prepareCurried(List(4, 5, 6)) _
doSomethingWithOtherArgs("abc")
  //> Slow processing of List(4, 5, 6)
  //> Do something quick with abc and List(processed 4, processed 5, processed 6)
doSomethingWithOtherArgs("cde")
  //> Slow processing of List(4, 5, 6)
  //> Do something quick with cde and List(processed 4, processed 5, processed 6)

我的问题是,我可以以某种方式强制prepareCurried在下面的行上进行评估吗?

val doSomethingWithOtherArgs = prepareCurried(List(4, 5, 6)) _

换句话说,当部分应用具有多个参数列表的函数时,是否可以获得与"evaluation on definition"相同的效果?

2 个答案:

答案 0 :(得分:10)

在这种情况下,从Scala的反射API中突破reify有助于了解语法如何被删除:

scala> import scala.reflect.runtime.universe.{ reify, showCode }
import scala.reflect.runtime.universe.{reify, showCode}

scala> showCode(reify(prepareCurried(List(4, 5, 6)) _).tree)
res0: String =
{
  val eta$0$1 = List.apply(4, 5, 6);
  ((s) => $read.prepareCurried(eta$0$1)(s))
}

如果没有preparedCurried(eta),根本无法获得s部分。当您考虑如何在JVM上表示具有多个参数列表的方法时(即所有参数列表都被粉碎在一起),这是有意义的。正如上面的评论者所说,如果您希望能够将第二个函数的准备与其应用程序分开,则需要明确地返回一个函数。

答案 1 :(得分:2)

如果我们将def prepareFunction(args: List[Any]): String => Unit = { println(s"Slow processing of $args...") val results = args.map(a => s"processed $a") (s: String) => println(s"Do something quick with $s and $results") } 的内部编写为函数文字而不是defs,我认为会更容易看到发生了什么。

这将是您的方法最初的样子:

def prepareCurried(args: List[Any]): Unit = (s: String) => {
  println(s"Slow processing of $args")
  val results = args.map(a => s"processed $a")

  println(s"Do something quick with $s and $results")
}

这是"等同于"你的咖喱方法:

prepareCurried

现在很容易看出,在{{1}}中,缓慢处理是正在返回的函数的一部分。

你无能为力。这是在使用多个参数列表而不是显式编写函数时丢失的灵活性。您必须为每个更适合的用例选择。