【问题标题】:Calling non-strict function in Scala with explicit types doesn't compile, inferred types works在 Scala 中使用显式类型调用非严格函数无法编译,推断类型有效
【发布时间】:2015-06-25 19:32:06
【问题描述】:

通过 Chiusano Rúnar Bjarnason 编写的出色的“Scala 中的 FP”,尝试通过 #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 中是否有任何语法,以便调用者可以发出“是的,这是按名称参数”的信号?

【问题讨论】:

    标签: scala types lazy-evaluation type-inference


    【解决方案1】:

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

    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 认为函数形式参数的类型属性是使用ParamType 语法规则生成的,而anonymous functions 参数的类型属性使用普通的Type 规则。 => by-name 指示器位于ParamType

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多