在类型类中,如何通过额外的参数改变操作?

时间:2014-07-23 20:47:08

标签: scala typeclass operation

在Scala类型类中,将有一个特征定义了操作,例如: NumberLike plus()minus()Transformer transform()AddressLabelMaker toLabel() {{1}}。然后可以使用类型类成员扩展特征。

通常,操作对于不同的成员将具有相同数量的参数,即签名看起来非常相似。我的问题是:如果一个成员需要基本上相同的操作,但是有一个额外的参数(可能只是一个隐含的参数:根据上下文修改操作的东西)会发生什么?

有没有办法在不定义全新(类似)类型类的情况下执行此操作?

3 个答案:

答案 0 :(得分:2)

使用额外的显式参数?不可以。每个类型类实例必须具有相同的接口,类型类的接口。有隐含参数?是。您可以声明返回所需隐式的implicit def,但本身需要隐式。这是一个解释的例子:

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 feed CatpersonFeederdef,可以创建隐式来提供Person,但需要隐式Silverware。所以在调用中:

feedAnimal(Person("John"))

编译器将搜索隐式AnimalFeeder[Person],找到personFeeder,然后搜索隐式Silverware,最后找到GoodSilver

请务必注意personFeeder 是隐式转化。尽管是从SilverwareAnimalFeeder[Person]的方法,但它永远不会隐式转换Silverware。这是因为其参数标记为implicit,隐式转换必须使其参数显式。

答案 1 :(得分:0)

我解决这个问题的方法是将参数包装在Params类型中,该类型具有必要的子类型。 Params成为类型类的第二个类型参数。所以现在类型类的成员可以拥有相同的操作签名。

不知道这是否是一种常见的解决方案?

以下是我所得到的一个例子。我不确定,也许这段代码可以改进。

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

答案 2 :(得分:0)

我不确定你的意思是什么,但如果它是关于改变一个类别类型的话题。实例的方法可以通过在调用方法之前以某种方式更改实例的条件来实现。

给定类型类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

修改IntPrinter以便它可以更改要打印的文字:

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

因此,您可以控制类型类方法的行为。