关于Scala中类型类的问题

时间:2011-04-12 13:59:26

标签: scala typeclass implicits

让我们有课程FruitOrangeApple

abstract class Fruit
class Orange extends Fruit
class Apple extends Fruit

现在,我想为writeOrange两种类型添加Apple功能。使用类型类模式,我可以执行以下操作:

trait Writer[T] {def write(t:T)}

implicit object AppleWriter extends Writer[Apple] {
   def write(a:Apple) {println("I am an apple!")} 
}

implicit object OrangeWriter extends Writer[Orange] {
   def write(o:Orange) {println("I am an orange!")} 
}

def write[T](t:T)(implicit w:Writer[T]){w.write(t)}

所以,非常好,但如果我想定义writeFruits怎么办?

def writeFruits(fruits:List[Fruit]) {for (fruit <- fruits) write(fruit)}

我希望writeFruits为每个write[Apple]拨打write[Orange]fruit。我发现它不起作用(我知道为什么)但是也许无论如何我都可以实现writeFruits

我能以某种方式实施writeFruits吗?

4 个答案:

答案 0 :(得分:6)

在协变/逆变类型的实例中,您几乎需要在“基础”类型上定义类型类:

implicit object FruitWriter extends Writer[Fruit] {
  def write(a : Fruit) = a match {
    case _ : Apple => println("I am an apple!")
    case _ : Orange => println("I am an orange")
  }
}

您还可以使用方差来定义类型类,以便在需要Writer [Apple]时可以使用Writer [Fruit]。这很不幸,但是如果你想使用OO多态,你必须将其编码到功能方面。

* 强文 *另一种选择是使用HList进行写水果并自己完成所有的类型递归......

假设:

trait HList
object HNil extends HList
case class ::[A, Rest <: HList](head : A, tail : Rest)

然后我们可以做一些有趣的事情:

implicit def nilWriter = new Writer[HNil] = { def write(o : HNil) = () }
implicit def hlistWriter[A, Rest](implicit aw : Writer[A], rw : Writer[Rest]) =
  new Writer[A :: Rest] {
  def write(o : (A :: Rest)) = {
    aw.write(o.head)
    rw.write(o.tail)
  }
}

现在

write( new Orange :: new Apple :: HNil)

注意:我没有测试过这段代码,但是递归跨越类型的概念是合理的。我实际上并没有推荐这种方法。

答案 1 :(得分:4)

您只需挑选Fruit存在Writer的{​​{1}}。不幸的是,一旦你转向Fruit,你就失去了自动找出哪个是哪个的能力。如果你必须以这种方式设置问题 - 而不是汇编可写水果或某些东西的列表 - 那么一个合理的选择是再次使用FruitWriter拆分类型:

def writeOne[T](t:T)(implicit w:Writer[T]){w.write(t)}  // New name to avoid shadowing

implicit object FruitWriter extends Writer[Fruit] {
  def write(f: Fruit) { f match {
    case o: Orange => writeOne(o)
    case a: Apple => writeOne(a)
  }}
}

scala> val fruits = List(new Apple, new Orange)
fruits: List[Fruit] = List(Apple@1148ab5c, Orange@39ea2de1)

scala> for (fruit <- fruits) writeOne(fruit)
I am an apple!
I am an orange!

答案 2 :(得分:2)

或者也许案例类适合你?

abstract class Fruit {}
case object Orange extends Fruit
case object Apple extends Fruit

trait Writer[T] {def write (t:T)}

implicit object FruitWriter extends Writer [Fruit] {
   def write (fruit: Fruit) = fruit match { 
     case Apple => println ("I am an apple!")
     case Orange => println ("I am an orange!")
   } 
}

def writeFruits (fruits: List[Fruit]) {
  for (fruit <- fruits) write(fruit)
}

val fl = List (Orange, Apple, Apple, Orange, Apple)    

writeFruits (fl)                                       
I am an orange!
I am an apple!
I am an apple!
I am an orange!
I am an apple!

答案 3 :(得分:1)

这不完全是你想要的,但是你可以自由地建立你的hiearchy:

sealed trait Fruit

case class Orange extends Fruit with OrangeWriter 
case class Apple extends Fruit
case class Banana extends Fruit

trait Writer {
  def write()
}

trait AppleWriter extends Writer {
  self: Apple =>
  def write() {println("I am an apple!")}
}

trait OrangeWriter extends Writer {
  self: Orange =>
  def write() {println("I am an orange!")}
}

def writeFruits(fruits:List[Fruit]) {
  fruits.collect{case w:Writer => w}.foreach(_.write())
}

writeFruits(List(Apple(), Orange(),Banana(), new Apple with AppleWriter))

正如您所看到的,您可以FruitWriter附加Orange(此处Apple s),您可以“动态”附加作家(最后一个{ {1}}中的{1}}。