繁体   English   中英

具有Lambda表达式的复杂Scala类型推断

[英]Complex Scala Type Inference w/ Lambda Expressions

我正在为一个我在Scala中构建的实验库开发一个DSL,而且我遇到了Scala类型推断的一些棘手的特性,因为它与lambda表达式参数有关,这些参数似乎没有在“ 编程 ”一书中介绍过。 在斯卡拉

在我的库中,我有一个特性Effect [-T],用于表示可以应用于T类对象的临时修饰符。我有一个对象myEffects,它有一个名为+ =的方法,它接受一个Effect [PlayerCharacter]类型的参数。 最后,我有一个泛型方法,当[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”方法。

在Scala编程的第16.10节中的理解是,编译器首先查看方法类型是否已知,如果是,它使用它来推断它的参数的预期类型。 在这种情况下,最外层的方法调用是+ =,它接受Effect [PlayerCharacter]类型的参数。 由于when [T]的返回类型是Effect [T],并且传递结果的方法需要Effect [PlayerCharacter]类型的参数,它可以推断T是PlayerCharacter,因此lambda的类型表达式作为第一个参数传递给“when”是PlayerCharacter => Boolean。 这似乎是在一个参数列表中提供参数时它是如何工作的,那么为什么将参数分成两个参数列表呢?

我自己对Scala比较陌生,而且我没有很多关于类型推理如何工作的详细技术知识。 所以最好带上一粒盐。

我认为区别在于编译器无法证明两个Tcondition : T => Boolean上是相同的condition : T => Booleaneffect : 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必须是PlayerCharacter 但在以下内容中:

myEffects += when(_.hpLow()) (applyModifierEffect)

编译器必须独立地计算出when(_.hpLow())的类型,以便它可以检查将它应用于applyModifierEffect是否有效。 而就其本身而言, _.hpLow()不为编译器提供足够的信息来推断,这是when[PlayerCharacter](_.hpLow())所以它不知道的是,返回类型是函数type 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方法,并且编译器已完成。

希望有更多知识渊博的人可以纠正我的细节,或者指出我是否完全咆哮了错误的树!

我有点困惑,因为在我看来,你说的任何版本都不应该工作,实际上我不能让它们中的任何一个工作。

类型推断从一个参数列表(非参数)到下一个参数列表从左到右工作。 典型的例子是集合中的方法foldLeft(比如Seq [A])

def foldLeft[B] (z: B)(op: (B, A) => B): B

z的类型将使B成为已知,因此可以在不指定B的情况下编写op(也不是从start开始知道的A,这是该类型的参数)。 如果例程写成

def foldLeft[B](z: B, op: (B,A) => B): B

或者作为

def foldLeft[B](op: (B,A) => B)(z: B): B

它不会工作,并且必须确保操作类型是明确的,或者在调用foldLeft时传递B明确。

在你的情况下,我认为最令人愉快的读取等价物是when一个Effect的方法,(或使它看起来像一个隐式转换),然后你会写

Effects += applyModifierEffect when (_.hpLow())

正如你提到效果是逆变的, when Effect方法不允许签名when (因为T => Boolean ,函数在其第一个类型参数中是逆变的,并且条件显示为参数,所以在逆变位置,两个逆变成为协变的,但它仍然可以用隐含的方式完成

object Effect {
  implicit def withWhen[T](e: Effect[T]) 
    = new {def when(condition: T => Boolean) = ...}
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM