在 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
上。
研究 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
上。