关于Scala中类型类的问题

Mic*_*ael 5 scala typeclass implicits

让有课Fruit,OrangeApple.

abstract class Fruit
class Orange extends Fruit
class Apple extends Fruit
Run Code Online (Sandbox Code Playgroud)

现在,我想添加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)}
Run Code Online (Sandbox Code Playgroud)

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

def writeFruits(fruits:List[Fruit]) {for (fruit <- fruits) write(fruit)}
Run Code Online (Sandbox Code Playgroud)

我想writeFruits给每一个write[Apple]write[Orange]每个人打电话fruit.我发现它不起作用(我知道为什么)但也许我可以实现它writeFruits.

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

jsu*_*eth 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")
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

假设:

trait HList
object HNil extends HList
case class ::[A, Rest <: HList](head : A, tail : Rest)
Run Code Online (Sandbox Code Playgroud)

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

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)
  }
}
Run Code Online (Sandbox Code Playgroud)

现在

write( new Orange :: new Apple :: HNil)
Run Code Online (Sandbox Code Playgroud)

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


Rex*_*err 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!
Run Code Online (Sandbox Code Playgroud)