In classes, how to change operations with additional parameters?

In Scala typeclass, a trait will be defined by which operations are defined, for example. NumberLikewith plus()and minus(), Transformerwith transform()or AddressLabelMakerwith toLabel(). You can then expand the characteristic using class elements.

Usually, operations will have the same number of parameters for different members, i.e. signatures will be very similar. My question is: what happens if a member needs what is essentially the same operation, but with an additional parameter (perhaps just implicit: something that modifies the operation based on context)?

Is there a way to do this without defining a whole new (similar) class?

+4
source share
3 answers

The way I solve this is to wrap the parameters in a Params type, which has subtypes as needed. Params becomes the second type of the typeclass parameter. So now, type class members can have the same signature for operations.

I wonder if this is a general solution?

Here is an example of what I get. I'm not sure, although perhaps 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"))
0
source

With an additional explicit parameter? No. Each typeclass instance must have the same interface as typeclass. With an implicit parameter? Yes. You can declare implicit defwhich returns the required implicit, but itself requires the implicit. Here is an example to explain:

case class Cat(name: String)
case class Person(name: String)
case class Silverware(kind: String)

implicit object GoodSilver extends Silverware("the good silver")

trait AnimalFeeder[A] {
  def feed(animal: A): Unit
}

implicit object CatFeeder extends AnimalFeeder[Cat] {
  def feed(cat: Cat) = println(cat.name + " eats cat food!")
}

implicit def personFeeder(implicit silverware: Silverware) =
  new AnimalFeeder[Person] {
    def feed(person: Person) =
      println(person.name + " eats people food with " + silverware.kind)
  }

def feedAnimal[A](a: A)(implicit feeder: AnimalFeeder[A]) = feeder.feed(a)

CatFeeder , cat Cat. personFeeder - def, Person, Silverware. , :

feedAnimal(Person("John"))

AnimalFeeder[Person], personFeeder, Silverware, GoodSilver.

, personFeeder . Silverware AnimalFeeder[Person], Silverware. , implicit, .

+2

, , , typeclass, , , , .

Printer[A]:

trait Printer[A] {
  def print(a: A): Unit
}
def print[A: Printer](a: A) = implicitly[Printer[A]].print(a)

Printer[Int], IntPrinter:

implicit object IntPrinter extends Printer[Int] {
  def print(a: Int) = s"integer $a"
}
print(3) //=> integer 3

Change IntPrinterso that he can change the text to print:

implicit object IntPrinter extends Printer[Int] {
  var text: String = "integer "
  def print(a: Int) = s"$text$a"
}
print(3) //=> integer 3
IntPrinter.text = ":"
print(3) //=> :3

This way you can control the behavior of typeclass` methods.

0
source

All Articles