关于Scala中类型类的问题

发布于 2024-10-31 19:13:22 字数 987 浏览 0 评论 0原文

假设有类 FruitOrangeApple

abstract class Fruit
class Orange extends Fruit
class Apple extends Fruit

现在我想为 OrangeApple 类型添加 write 功能。使用 type class 模式,我可以执行以下操作:

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 为每个 fruit 调用 write[Apple]write[Orange]。我发现它不起作用(我知道为什么),但是也许我无论如何都可以实现writeFruits

我可以以某种方式实现 writeFruits 吗?

Let there are classes Fruit, Orange, and Apple.

abstract class Fruit
class Orange extends Fruit
class Apple extends Fruit

Now I want to add write functionality to both types Orange and Apple. Using the type class pattern I can do the following:

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

So for, so good but what if I want to define writeFruits ?

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

I would like writeFruits to call either write[Apple] or write[Orange] for each fruit. I see that it does not work (and I know why) but maybe I can implement the writeFruits anyway.

Can I implement writeFruits somehow ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

横笛休吹塞上声 2024-11-07 19:13:22

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

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 时可以使用 Writer[Fruit] [苹果]。不幸的是,如果您想使用面向对象的多态性,则必须将其编码到功能方面。

*强文本*另一种选择是使用 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)

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

In the instance of covariant/contravariant types, you almost need to define your type class on the "base" type here:

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

You could also work on defining the type class with variance so that Writer[Fruit] could be used when you need a Writer[Apple]. It's unfortunate, but if you want to use OO polymorphism, you have to encode that into the functional aspects.

*strong text*Another option is to use an HList for write-fruits and do all the type-recursion yourself...

Assuming:

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

Then we can do something fun like:

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

NOW

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

Note: I have not tested this code, but the concept of recursively spanning types is sound. I'm not actually recommending this approach.

笑脸一如从前 2024-11-07 19:13:22

您只需挑选出存在 Writer 的那些 Fruit。不幸的是,一旦你转换为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!

You need to pick out only those Fruit for which a Writer exists. Unfortunately, once you've cast to Fruit you've lost the ability to automatically figure out which is which. If you must set up the problem this way--rather than assembling a list of writable fruit or somesuch--then one reasonable option is to split out the types again with a 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!
看轻我的陪伴 2024-11-07 19:13:22

或者也许案例课程适合您?

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!

Or maybe case-classes are for you?

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!
海风掠过北极光 2024-11-07 19:13:22

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

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

如您所见,您可以拥有始终附加一个 WriterFruit (此处是 Orange),您可以“即时”附加 Writers(List 中的最后一个 Apple)。

This is not exactly what you want, but gives you a lot of freedom to build your 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))

As you can see, you can have Fruits which have always a Writer attached (here Oranges) and you can attach Writers "on the fly" (the last Apple in the List).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文