5

让有类Fruit,OrangeApple

abstract class Fruit
class Orange extends Fruit
class Apple extends Fruit

现在我想为write这两种类型OrangeApple. 使用类型类模式,我可以执行以下操作:

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

4 回答 4

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 进行 write-fruits 并自己进行所有类型递归...

假设:

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)

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

于 2011-04-12T14:14:50.250 回答
4

您只需要挑选出存在Fruita 的那些Writer。不幸的是,一旦你投了,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!
于 2011-04-12T14:19:51.537 回答
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!
于 2011-04-12T14:33:34.147 回答
1

这不完全是您想要的,但给了您很大的自由来建立您的层次结构:

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

如您所见,您可以Fruit拥有始终Writer附加的 s(此处为Oranges),并且您可以“即时”附加 Writers(最后一个AppleList

于 2011-04-13T08:33:55.363 回答