Scala 何时需要匿名函数和扩展函数的参数类型?

发布于 2024-10-16 01:30:47 字数 432 浏览 2 评论 0原文

Scala编译器什么时候真正需要匿名函数参数的类型信息?

例如,给定这个函数:

def callOn[T,R](target: T, f: (T => R)) = f(target)

那么我不能像这样使用它:

callOn(4, _.toString)
  => error: missing parameter type for expanded function ((x$1) => x$1.toString)

我必须指定

callOn(4, (_: Int).toString)

哪个函数相当难看。为什么我的示例不起作用,而集合类上的 map、filter、foldLeft 等方法似乎不需要这种显式类型?

When does the Scala compiler really need the type information of parameters of anonymous functions?

For instance, given this function:

def callOn[T,R](target: T, f: (T => R)) = f(target)

then I cannot use it like this:

callOn(4, _.toString)
  => error: missing parameter type for expanded function ((x$1) => x$1.toString)

and I have to specify

callOn(4, (_: Int).toString)

which is rather ugly. Why doesn't my example work, whereas method like map, filter, foldLeft, etc. on the collection classes don't seem to need this explicit type?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

舂唻埖巳落 2024-10-23 01:30:47

类型推理的技巧是将其视为迭代细化的过程。每个参数块可用于推断一些类型参数,然后可在后续块中使用这些参数。因此采用以下定义:

def chain[T,A,B](x: T)(fn1: T=>A)(fn2: A=>B) = fn2(fn1(x))

称为:

chain(2)(_*10)("xxx"+_)

那么这是如何推断的呢?首先,我们从已知类型为 Int 的块 (2) 开始。将其代回 T 参数,我们得到:

def chain[A,B](x: Int)(fn1: Int=>A)(fn2: A=>B) = fn2(fn1(x))

下一个参数块是 (_*10),我们现在知道占位符 _ 的类型code> 为 Int...并将 Int 乘以 Int 得到另一个 Int。即使发生溢出,返回的类型也是如此;在极端情况下,它可能会抛出异常,但异常的类型为 Nothing,它是类型系统中其他所有内容的子类,因此我们仍然可以说 Nothing 是 - Int 并且推断的 Int 类型仍然有效。

推断出A后,方法就变成:

def chain[B](x: Int)(fn1: Int=>Int)(fn2: Int=>B) = fn2(fn1(x))

只留下可以从("xxx"+_)推断出的B。由于 String + IntString,因此该方法现在是:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String) = fn2(fn1(x))

由于该方法的返回类型直接来自 fn2,因此也可以是为了完整性而明确显示:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String): String = fn2(fn1(x))

现在您已经看到了,所有类型都已安全解析,并且该方法被证明是静态有效的。


在您的情况下,您需要先推断出类型 T,然后才能从类型 T=>R 推断出 R。为此,您必须将参数分成两个不同的块,以柯里化形式编写该方法:

def callOn[T,R](target: T)(f: (T => R)) = f(target)

The trick to type inference is to consider it as an process of iterative refinement. Each parameter block can be used to infer some of the type parameters, which can then be used in subsequent blocks. So take the following definition:

def chain[T,A,B](x: T)(fn1: T=>A)(fn2: A=>B) = fn2(fn1(x))

called as:

chain(2)(_*10)("xxx"+_)

So how is this inferred? First, we start with the block (2) which is known to have the type Int. Substituting that back into the T parameter we get:

def chain[A,B](x: Int)(fn1: Int=>A)(fn2: A=>B) = fn2(fn1(x))

The next parameter block is (_*10), where we now know the type of the placeholder _ to be Int... and multiplying an Int by an Int gives another Int. This is true of the returned type even if an overflow occurrs; at the extreme end it may throw an exception, but exceptions have the type Nothing which is a subclass of everything else in the type system, so we can still say Nothing is-an Int and the inferred type of Int is still valid.

With A inferred, the method becomes:

def chain[B](x: Int)(fn1: Int=>Int)(fn2: Int=>B) = fn2(fn1(x))

Leaving only B which can be inferred from ("xxx"+_). As String + Int is a String, the method is now:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String) = fn2(fn1(x))

As the return type of the method comes direct from fn2, that can also be explicitly shown for completeness:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String): String = fn2(fn1(x))

There you have it, all types safely resolved, and the method proven to be statically valid.


In your case, you need the type T to be inferred before it's possible to infer R from the type T=>R. To do this you must split out the parameters into two distinct blocks, writing the method in a curried form:

def callOn[T,R](target: T)(f: (T => R)) = f(target)
多孤肩上扛 2024-10-23 01:30:47

这个问题也在这里得到了解答:

​​传递所有适用类型的函数

您期望...Scala 编译器将两个参数考虑两次以推断正确的类型。但 Scala 并没有这样做——它只使用从一个参数列表到下一个参数列表的信息,而不是从一个参数到下一个参数的信息。这意味着参数 f 和 a [WS:本例中为目标和 f] 是独立分析的,而不具有知道另一个是什么的优势。

请注意,它确实适用于柯里化版本:

scala> def callOn[T,R](target: T)(f: (T => R)) = f(target)
callOn: [T,R](target: T)(f: (T) => R)R

scala> callOn(4)(_.toString)
res0: java.lang.String = 4

scala> 

This question has also been answered here:

Passing functions for all applicable types around

You expect, ... for Scala's compiler to take into account both parameters to twice to infer the correct types. Scala doesn't do that, though -- it only uses information from one parameter list to the next, but not from one parameter to the next. That mean the parameters f and a [WS: target and f in this case] are analyzed independently, without having the advantage of knowing what the other is.

Note that it does work with a curried version:

scala> def callOn[T,R](target: T)(f: (T => R)) = f(target)
callOn: [T,R](target: T)(f: (T) => R)R

scala> callOn(4)(_.toString)
res0: java.lang.String = 4

scala> 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文