Search code examples
scalaabstract-type

Advise on exercise with scala abstract types


I'm looking for an advise on this exercise I'm doing - specifically, what areas can be improved/shortened? I don't like that to feed the animals I have to use asInstanceOf but cannot find a better way. An idea is that I need to run this function for millions of objects so performance is important.

P.S. Would generics perform better in my case?

trait Animal {
  type suitableFood
  def eat(f: suitableFood)
}
trait Food
case class Grass (val color: String) extends Food
case class Fish (val size: String, val condition: String) extends Food

trait GrassT{
  type suitableFood = Grass
}
trait FishT{
  type suitableFood = Fish
}
class Cow extends Animal with GrassT{
  def eat (f: suitableFood) = {
    println("moo " + f.color)
  }
}
class Sheep extends Animal with GrassT{
  def eat (f: suitableFood) = {
    println("baaa " + f.color)
  }
}
class Cat extends Animal with FishT{
  def eat (f: suitableFood) = {
    println("mew " + f.size + " " + f.condition)
  }
}
val cow = new Cow
val sheep = new Sheep
val whiteCat = new Cat
val blackCat = new Cat
val fish = new Fish("small", "fresh")
val grass = new Grass("green")
val cats = List(whiteCat, blackCat)
val farmAnimals = List(sheep, cow)
def feedAnimals[F <: Food](food: F, animals: List[Animal]) = {
  animals.foreach(a => a.eat(food.asInstanceOf[a.suitableFood]))
}
feedAnimals(grass, farmAnimals)
feedAnimals(fish, cats)

Solution

  • You just need to put a stricter type restriction on feedAnimals:

    def feedAnimals[F <: Food](food: F, animals: List[Animal { type suitableFood = F }]) = 
        animals.foreach(_.eat(food))
    

    Or:

    def feedAnimals[F <: Food, A <: Animal { type suitableFood = F }](food: F, animals: List[A]) = 
        animals.foreach(_.eat(food))
    

    Unlike the solution using asInstanceOf, these solutions will fail at compile time (as opposed to runtime) for statements like feedAnimals(grass, cats), as desired.

    Generics might be simpler in this case, if you prefer.