In a Scala typeclass, there will be a trait on which operations are defined, e.g. NumberLike
with plus()
and minus()
, Transformer
with transform()
, or AddressLabelMaker
with toLabel()
. It's then possible to extend the trait with typeclass members.
Typically the operations will have the same number of parameters for different members, i.e. the signatures will look very similar. My question is: what happens if a member needs what is essentially the same operation, but with an extra parameter (maybe just an implicit one: something that modifies the operation based on the context) ?
Is there a way to do this without defining a whole new (similar) typeclass?
The way I am solving this is to wrap the parameters in a type Params, which has subtypes as necessary. Params becomes the second type parameter of the typeclass. So now members of the typeclass can have the same signature for operations.
Wonder if this is a common solution?
Here is what an example of what I am getting at. I'm not sure though, maybe this code can be improved.
trait Animal
case class Cat(name: String) extends Animal
case class Person(name: String) extends Animal
case class Silverware(kind: String)
trait FeederParams
case class CatFeederParams() extends FeederParams
case class PersonFeederParams(val silverware: Silverware) extends FeederParams
trait AnimalFeeder[A <: Animal, P <: FeederParams] {
def feed(animal: A)(implicit params: P): Unit
}
implicit object CatFeeder extends AnimalFeeder[Cat, CatFeederParams] {
def feed(cat: Cat)(implicit params: CatFeederParams) =
println(cat.name + " eats cat food!")
}
implicit object PersonFeeder extends AnimalFeeder[Person, PersonFeederParams] {
def feed(person: Person)(implicit params: PersonFeederParams) =
println(person.name + " eats people food with " + params.silverware.kind)
}
def feedAnimal[A <: Animal, P <: FeederParams](a: A)(implicit feeder: AnimalFeeder[A, P], params: P) =
feeder.feed(a)
implicit object personParams extends PersonFeederParams(Silverware("the good silver"))
implicit object catParams extends CatFeederParams()
feedAnimal(Person("John"))
feedAnimal(Cat("Garfield"))