如何替换通用匿名函数?

假设有腿动物有一个特征:

trait Legged {
  val legs: Int

  def updateLegs(legs: Int): Legged
}

有两种这样的有腿的动物:

case class Chicken(feathers: Int, legs: Int = 2) extends Legged {
  override def updateLegs(legs: Int): Legged = copy(legs = legs)
}

case class Dog(name: String, legs: Int = 4) extends Legged {
  override def updateLegs(legs: Int): Legged = copy(legs = legs)
}

在农场里还有这些动物的持有人

case class Farm(chicken: Chicken, dog: Dog)

以及一种通过添加一条额外的腿来变异所有有腿动物的通用方法

def mutate(legged: Legged): Legged = legged.updateLegs(legged.legs + 1)

问题是如何在 上实现一个方法,Farm以便将mutate: Legged => Legged函数作为参数并将其应用于所有Legged动物?

val farm = Farm(Chicken(1500), Dog("Max"))
farm.mapAll(mutate) //this should return a farm whose animals have an extra leg

到目前为止我所带来的,但实际上并没有用

trait LeggedFunc[T <: Legged] extends (T => T)


case class Farm(chicken: Chicken, dog: Dog) {
  def mapAll(leggedFunc: LeggedFunc[Legged]): Farm = {
    //todo how to implement?
    val c = leggedFunc[Chicken](chicken)
  }
}

我知道如何使用模式匹配来做到这一点,但这会导致潜在的MatchError.

回答

一种可能的方法(类型安全,不使用asInstanceOf)可能是使用依赖于对象的类型。

首先,我们应该添加一个抽象成员来使用Legged子类的具体类型:

sealed trait Legged { self =>
type Me >: self.type <: Legged // F-Bounded like type, Me have to be the same type of the subclasses
val legs: Int
def updateLegs(legs: Int): Me
}

然后,Legged子类变成了:


case class Chicken(feathers: Int, legs: Int = 2) extends Legged {
type Me = Chicken
override def updateLegs(legs: Int): Chicken = copy(legs = legs)
}
case class Dog(name: String, legs: Int = 4) extends Legged {
type Me = Dog
override def updateLegs(legs: Int): Dog = copy(legs = legs)
}

这样,就可以定义一个函数,该函数返回所传递的腿的具体子类(类似于@Gaël J 所做的):

trait LeggedFunc {
def apply(a : Legged): a.Me
}
val mutate = new LeggedFunc { override def apply(legged: Legged): legged.Me = legged.updateLegs(legged.legs + 1) }

最后,这个Farm类很简单,定义为:

case class Farm(chicken: Chicken, dog: Dog) {
def mapAll(leggedFunc: LeggedFunc): Farm = {
val c : Chicken = leggedFunc(chicken)
val d : Dog = leggedFunc(dog)
Farm(c, d)
}
}

Scala 2 的Scastie

但是为什么是对象依赖类型呢?在 Scala 3.0 中,可以定义dependent function type为:

type LeggedFunc = (l: Legged) => l.Me
val mutate : LeggedFunc = (l) => l.updateLegs(l.legs + 1)

使这个解决方案(依赖于对象的类型)更清晰和类型安全。

Scastie for Scala 3 版本

我希望我能帮助你:)


以上是如何替换通用匿名函数?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>