我正在为我在 Scala 中构建的实验库开发 DSL,我遇到了 Scala 类型推断的一些令人烦恼的特性,因为它与 Programming In Scala 书中似乎没有涵盖的 lambda 表达式参数有关.
在我的库中,我有一个特征 Effect[-T],用于表示可应用于 T 类型对象的临时修饰符。我有一个对象 myEffects,它有一个名为 += 的方法,它接受一个Effect[PlayerCharacter] 类型的参数。最后,我有一个通用方法 when[T],它用于通过接受条件表达式和另一个效果作为参数来构造条件效果。签名如下:
def when[T](condition : T => Boolean) (effect : Effect[T]) : Effect[T]
当我使用上述签名调用“when”方法并将其结果传递给 += 方法时,它无法推断 lambda 表达式的参数类型。
myEffects += when(_.hpLow()) (applyModifierEffect) //<-- Compiler error
如果我将“when”的参数组合到一个参数列表中,Scala 能够很好地推断出 lambda 表达式的类型。
def when[T](condition : T => Boolean, effect : Effect[T]) : Effect[T]
/* Snip */
myEffects += when(_.hpLow(), applyModifierEffect) //This works fine!
如果我完全删除第二个参数,它也可以工作。
def when[T](condition : T => Boolean) : Effect[T]
/* Snip */
myEffects += when(_.hpLow()) //This works too!
但是,出于美学原因,我真的希望将参数作为单独的参数列表传递给“when”方法。
我从 Programming in Scala 的第 16.10 节的理解是,编译器首先查看方法类型是否已知,如果是,它使用它来推断它的参数的预期类型。在这种情况下,最外层的方法调用是 +=,它接受 Effect[PlayerCharacter] 类型的参数。由于 when[T] 的返回类型是 Effect[T],并且传递结果的方法需要一个 Effect[PlayerCharacter] 类型的参数,它可以推断 T 是 PlayerCharacter,因此是 lambda 的类型作为“when”的第一个参数传递的表达式是 PlayerCharacter => Boolean。当参数在一个参数列表中提供时,这似乎是它的工作原理,那么为什么将参数分成两个参数列表会破坏它呢?
最佳答案
我自己对 Scala 比较陌生,而且我对类型推断的工作原理没有很多详细的技术知识。所以最好把这个和一粒盐一起吃。
我认为不同之处在于编译器无法向自己证明这两个 T
condition : T => Boolean
中的 s 相同和 effect : Effect[T]
, 在双参数列表版本中。
我相信当您有多个参数列表时(因为 Scala 将其视为定义一个返回使用下一个参数列表的函数的方法)编译器一次处理一个参数列表,而不是像在单个参数列表版本中那样全部一起处理。
所以在这种情况下:
def when[T](condition : T => Boolean, effect : Effect[T]) : Effect[T]
/* Snip */
myEffects += when(_.hpLow(), applyModifierEffect) //This works fine!
applyModifierEffect
的类型以及所需的参数类型myEffects +=
可以帮助约束_.hpLow()
的参数类型;所有T
s 必须是 PlayerCharacter
.但在下面:myEffects += when(_.hpLow()) (applyModifierEffect)
编译器必须找出
when(_.hpLow())
的类型独立,以便它可以检查将其应用于 applyModifierEffect
是否有效.就其本身而言,_.hpLow()
没有为编译器提供足够的信息来推断这是 when[PlayerCharacter](_.hpLow())
, 所以它不知道返回类型是 Effect[PlayerCharacter] => Effect[PlayerCharacter]
类型的函数,因此它不知道在该上下文中应用该函数是否有效。我的猜测是,类型推断只是没有连接点,并找出恰好有一种类型可以避免类型错误。至于另一种有效的情况:
def when[T](condition : T => Boolean) : Effect[T]
/* Snip */
myEffects += when(_.hpLow()) //This works too!
这里返回类型
when
和它的参数类型更直接相连,不需要经过currying创建的额外函数的参数类型和返回类型。由于myEffects +=
需要 Effect[PlayerCharacter]
, T
必须是 PlayerCharacter
,其中有 hpLow
方法,编译完成。希望有更多知识的人可以纠正我的细节,或者指出我是否完全吠错了树!
关于scala - 使用 Lambda 表达式的复杂 Scala 类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7655818/