Scala如何将案例类转换为函数?

use*_*046 2 scala function currying

我试图了解如何将案例类作为参数传递给接受函数作为参数的函数。下面是一个示例:

考虑以下功能

def !![B](h: Out[B] => A): In[B] = { ... }
Run Code Online (Sandbox Code Playgroud)

如果我正确理解,这是一种多态方法,它具有类型参数,B并且接受函数h作为参数。Out并且In被其他两个类前所定义。

然后,如下所示使用此功能:

case class Q(p: boolean)(val cont: Out[R])
case class R(p: Int)

def g(c: Out[Q]) = {
  val rin = c !! Q(true)_
  ...
}
Run Code Online (Sandbox Code Playgroud)

我知道使用currying是为了避免编写类型注释,而只是编写_。但是,我无法理解为什么以及如何将案例类Q转换为type的函数(h)Out[B] => A

编辑1更新!以上以及InOut定义:

abstract class In[+A] {
  def future: Future[A]
  def receive(implicit d: Duration): A = {
    Await.result[A](future, d)
  }
  def ?[B](f: A => B)(implicit d: Duration): B = {
    f(receive)
  }
}
Run Code Online (Sandbox Code Playgroud)
abstract class Out[-A]{
  def promise[B <: A]: Promise[B]
  def send(msg: A): Unit = promise.success(msg)
  def !(msg: A) = send(msg)
  def create[B](): (In[B], Out[B])
}
Run Code Online (Sandbox Code Playgroud)

这些代码示例摘自以下论文:http : //drops.dagstuhl.de/opus/volltexte/2016/6115/

Yuv*_*kov 5

TLDR;

使用具有多个参数列表的case类并部分应用它会产生部分应用的apply调用+ eta扩展,这会将方法转换为函数值:

val res: Out[Q] => Q = Q.apply(true) _
Run Code Online (Sandbox Code Playgroud)

更长的解释

要了解它在Scala中的工作方式,我们必须了解案例类背后的一些基础知识以及方法和函数之间的区别。

Scala中的案例类是表示数据的一种紧凑方式。定义案例类时,您会得到一堆便捷方法,这些方法是由编译器为您创建的,例如hashCodeequals

此外,编译器还会生成一个名为的方法apply,该方法使您无需使用new关键字即可创建案例类实例:

case class X(a: Int)

val x = X(1)
Run Code Online (Sandbox Code Playgroud)

编译器将此调用扩展为

val x = X.apply(1)
Run Code Online (Sandbox Code Playgroud)

您的案例类将发生相同的事情,只是您的案例类具有多个参数列表:

case class Q(p: boolean)(val cont: Out[R])

val q: Q = Q(true)(new Out[Int] { })
Run Code Online (Sandbox Code Playgroud)

将被翻译成

val q: Q = Q.apply(true)(new Out[Int] { })
Run Code Online (Sandbox Code Playgroud)

最重要的是,Scala可以将非值类型的方法转换为类型为FunctionXX 的函数类型的函数类型。为了将方法转换为函数值,我们使用了称为eta扩展的技巧,在此方法下划线称为方法。

def foo(i: Int): Int = i

val f: Int => Int = foo _
Run Code Online (Sandbox Code Playgroud)

这会将方法foo转换为type的函数值Function1[Int, Int]

现在我们已经掌握了这些知识,让我们回到您的示例:

val rin = c !! Q(true) _
Run Code Online (Sandbox Code Playgroud)

如果仅在此处隔离Q,则此调用将转换为:

val rin = Q.apply(true) _
Run Code Online (Sandbox Code Playgroud)

由于该apply方法包含多个参数列表,因此我们将返回一个给定的函数Out[Q],它将创建一个Q

val rin: Out[R] => Q = Q.apply(true) _
Run Code Online (Sandbox Code Playgroud)