在 Scala 中使用显式类型调用非严格函数不会编译,推断类型有效

Calling non-strict function in Scala with explicit types doesn't compile, inferred types works

研究 Chiusano Rúnar Bjarnason 的优秀 "FP in Scala",在尝试通过 #foldRight 懒惰地实现 Stream#takeWhile 时出现了一个奇怪的编译错误。鉴于本书中的以下代码(也在 GitHub 上):

trait Stream[+A] {
  def foldRight[B](z: => B)(f: (A, => B) => B): B = 
    this match {
      case Cons(h,t) => f(h(), t().foldRight(z)(f))
      case _ => z
}

case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]

object Stream {
  def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = {
    lazy val head = hd
    lazy val tail = tl
    Cons(() => head, () => tail)
  def empty[A]: Stream[A] = Empty
}

我试过了:

def takeWhile_fold(p: A => Boolean): Stream[A] =
  foldRight(empty[A])((it:A, acc:Stream[A]) => if (p(it)) cons(it, acc) else acc)

但这给出了编译错误:

type mismatch;
[error]  found   : (A, fpinscala.laziness.Stream[A]) => fpinscala.laziness.Stream[A]
[error]  required: (A, => fpinscala.laziness.Stream[A]) => fpinscala.laziness.Stream[A]
[error]       foldRight(empty[A])((it:A, acc:Stream[A]) => if (p(it)) cons(it, acc) else ac

但是如果我删除 lambda 上的类型,它会起作用:

def takeWhile_fold(p: A => Boolean): Stream[A] =
  foldRight(empty[A])((it, acc) => if (p(it)) cons(it, acc) else acc)

Scala 无法在调用代码中声明它应该是一个按名称的参数,是吗? (它甚至对调用者有意义吗,是接收者决定的,对吧?)尝试:

def takeWhile_fold(p: A => Boolean): Stream[A] =
  foldRight(empty[A])((it, acc: => Stream[A]) => if (p(it)) cons(it, acc) else acc)

给出另一个编译错误:

[error]      identifier expected but '=>' found.
[error]       foldRight(empty[A])((it, acc: => Stream[A]) => if (p(it)) cons(it, acc) else acc)
                                            ^
[error]  ')' expected but '}' found.
[error] }
[error] ^
[error] two errors found

我已经解决了这个练习,但我的问题是 - 为什么它不适用于显式类型?他们是否以某种方式强制执行严格性,而接收者必须具有非严格性?如果是这样,Scala 中是否有任何语法以便调用者可以发出信号 "yes, this is by-name parameters"?

是的,名字最终成为签名的一部分。您可以想象编译器通过翻译来实现按名称的参数:

def foo(n: => Int) = ... n ...

对此:

def foo(n: () => Int) = ... n() ...

这样做的一个副作用是,如果您多次引用按名称的参数,它们将被计算多次。所以这个:

def bar(n: => Int) = n + n
bar( { println("foo"); 5 } )

在返回 10 之前会打印两次 foo。与此相同:

def bar(n: () => Int) = n() + n()
bar { () => println("foo"); 5 }

至于您是否可以明确声明 lambda 接受按名称的参数...我不确定。我试过这个:

def foo( f: ( => Int) => Int ) = f({ println("foo"); 5 })
foo { (a) => a + a }

哪个有效。我尝试了这些:

foo { (a : Int ) => a + a }
foo { (a : => Int ) => a + a }

失败了。

规范似乎 indicate that the type property of formal parameters to functions are generated with the ParamType syntax rule, whereas the type property of anonymous functions 参数使用普通的 Type 规则。 => 别名指标在 ParamType 上。