Scala中的部分函数应用

时间:2016-08-22 07:51:32

标签: scala functional-programming partialfunction

我正在学习功能编程,请阅读Paul Chiusano和RúnarBjarnason撰写的“Scala中的函数编程”一书。我特别在第3章,我将一些伴侣函数实现到代表单链表的类中,作者提供了这些函数。

package fpinscala.datastructures

sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]

object List {
    def sum(ints: List[Int]): Int = ints match {
    case Nil => 0
    case Cons(x,xs) => x + sum(xs)
    }

    def product(ds: List[Double]): Double = ds match {
    case Nil => 1.0
    case Cons(0.0, _) => 0.0
    case Cons(x,xs) => x * product(xs)
    }

    def apply[A](as: A*): List[A] =
    if (as.isEmpty) Nil
    else Cons(as.head, apply(as.tail: _*))

    def tail[A](ls: List[A]): List[A] = ls match {
    case Nil => Nil
    case Cons(x,xs) => xs
    }
... (more functions)
}

我正在实现的函数进入对象List,是伴侣函数

实现dropWhile时,其方法签名为:

def dropWhile[A](l: List[A])(f: A => Boolean): List[A]

我遇到了一些关于部分功能应用的问题:

在书中,作者说谓词f在一个单独的参数组中传递,以帮助scala编译器进行类型推断,因为如果我们这样做,Scala可以确定f的类型而没有任何注释,基于它知道List的类型,这使得函数使用起来更方便。

所以,如果我们在相同的参数组中传递f,scala将强制调用变为这样:val total = List.dropWhile(example, (x:Int) => 6%x==0 )我们明确定义x的类型,我们将“失去”部分函数的可能性申请,我是对的吗?

但是,为什么部分功能应用在这种情况下有用?只允许类型推断?在没有应用谓词f的情​​况下“部分应用”像dropWhile这样的函数是否有意义?因为在我看来,如果我们不应用f ...那么计算在被使用之前就会“停止”...

那么......为什么部分功能应用程序有用?这是它总是如何完成或者只是Scala特有的东西?我知道Haskell有一个叫做“完全推理”的东西,但我不确切地知道它的含义......

提前致谢

1 个答案:

答案 0 :(得分:3)

那里散布着几个问题,所以我会尝试单独回答。

关于类型推断,是的,分离参数列表有助于编译推断f的类型。

这是因为Scala具有线性局部类型推断(从左到右),它使用第一个参数列表来推断A(来自l的类型)。然后,它可以使用此信息来推断f的类型。

举个例子

dropWhile(List(1, 2, 3))(x => x < 3)

编译器将执行以下步骤:

  • 第一个参数列表

    • A未知。
    • 预计会List[A]
    • 提供了List[Int](这由List中的元素类型推断)
    • =&GT; AInt
  • 第二个参数列表

    • 我们知道A = Int
    • 所以我们希望函数Int => Booleanf

如果不将两个参数列表分开,编译器不能“停止”并在类型检查A之前决定f的类型。在确定f的类型时,A将成为“对话”的一部分,因此您需要对其进行注释。

这是Haskell可以做得更好的事情,因为它使用了一个不同的类型系统(Hindley-Milner),它也可以使用从应用该函数的上下文派生的信息。这就是为什么它也被称为“完整”或“普遍”。

为什么Scala没有Hindley-Milner型系统?长话短说,因为Scala还支持子类型,这种类型系统几乎不存在。更多关于这个问题:

关于部分申请,“为什么有用”的问题肯定太宽泛,无法在这里得到解答。但是,在特定的dropWhile情况下,假设您有一个表示不同“丢弃”条件的函数列表。使用部分应用的功能,您可以:

val list = List(1, 2, 3)
val conditions: List[Int => Boolean] = List(_ < 1, _ < 2, _ < 3)
conditions.map(dropWhile(list)) // List(List(1, 2, 3), List(2, 3), List(3))

显然,使用非咖喱功能(即单个参数列表),您可以实现相同的

val list = List(1, 2, 3)
val conditions: List[Int => Boolean] = List(_ < 1, _ < 2, _ < 3)
conditions.map(cond => dropWhile(list, cond))

但是在编写函数时,currying可以提供更大的灵活性。

有关此主题的更多信息: