scalaanonymous-functionfunction-signature

How to substitute generic anonymous functions?


Suppose there is a trait for legged animals:

trait Legged {
  val legs: Int

  def updateLegs(legs: Int): Legged
}

And there two such legged animals:

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)
}

There is also a holder for these animal, in a farm

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

And a generic method to mutate all the legged animals by adding them one extra leg

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

The question is how to implement a method on the Farm so that it takes the mutate: Legged => Legged function as a parameter and applies it to all the Legged animals?

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

What I've come with thus far, but it doesn't actually work

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)
  }
}

I know how to do it with patter matching, but that leads to potential MatchError.


Solution

  • A possible way to do that (type-safely, without using asInstanceOf) could be using object-dependant type.

    First of all, we should add an abstract member that uses the concrete type of Legged subclasses:

    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
    }
    

    Then, the Legged subclasses became:

    
    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)
    }
    

    In this way, it is possible to define a function that returns the concrete subclass of the Legged passed (similar to what @Gaël J done):

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

    Finally, the Farm class is straightforward defined as:

    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)
      }
    }
    

    Scastie for Scala 2

    But why object-dependent type? In Scala 3.0, it is possible to define dependent function type as:

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

    Making this solution (object-dependent type) cleaner and type-safe.

    Scastie for Scala 3 version