5

クラスFruitOrange、およびがあるとしAppleます。

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

どちらかまたはfor eachwriteFruitsを呼び出したいと思います。うまくいかないことがわかりました(そしてその理由はわかっています)が、とにかく実装できるかもしれません。write[Apple]write[Orange]fruitwriteFruits

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] を使用できるように、変数を使用して型クラスを定義することもできます。残念ながら、オブジェクト指向ポリモーフィズムを使用する場合は、それを機能面にエンコードする必要があります。

*強いテキスト* もう 1 つのオプションは、書き込みフルーツに 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)

注: このコードはテストしていませんが、型を再帰的にスパンするという概念は適切です。私は実際にこのアプローチを推奨していません。

于 2011-04-12T14:14:50.250 に答える
4

が存在するものだけを選択する必要がありFruitますWriter。残念ながら、一度キャストするFruitと、どれがどれであるかを自動的に判断する機能が失われます。書き込み可能な果物などのリストを組み立てるのではなく、この方法で問題を設定する必要がある場合、1 つの合理的なオプションは、次のように型を再度分割すること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) を持つことができ、「その場で」ライター ( の最後) をアタッチできAppleますList

于 2011-04-13T08:33:55.363 に答える