简体   繁体   English

使用类型类扩展案例类

[英]Extend case class with typeclass

I have this code我有这个代码

  case class Salary(employee: String, amount: Double){

  }

  trait XN[M] {
    def x2(m: M): M
    def x3(m: M): M
    def x4(m: M): M
  }

    

I want to extend Salary with XN trait in order for the following test to work:我想用 XN 特性扩展 Salary,以便以下测试工作:

test("Salary is extended with xN") {

    val bobSalary = Salary("Bob", 100.0)

    bobSalary.x2 shouldBe Salary("Bob", 200.0)
    bobSalary.x3 shouldBe Salary("Bob", 300.0)
    bobSalary.x4 shouldBe Salary("Bob", 400.0)

  }

My attempts:我的尝试:

#1 #1

  implicit val SalaryXN: XN[Salary] = new XN[Salary] {
    override def x2(m: Salary): Salary = m.copy(amount = m.amount * 2)

    override def x3(m: Salary): Salary = m.copy(amount = m.amount * 3)

    override def x4(m: Salary): Salary = m.copy(amount = m.amount * 4)
  }

#2 #2

  object Salary extends XN[Salary] {
    override def x2(m: Salary): Salary = new Salary(employee = m.employee, amount = m.amount * 2)

    override def x3(m: Salary): Salary = new Salary(employee = m.employee, amount = m.amount * 3)

    override def x4(m: Salary): Salary = new Salary(employee = m.employee, amount = m.amount * 4)
  }

How to do that?怎么做?

Online code在线代码

[Use Accepted solution, implicits are tricky!] [使用公认的解决方案,隐式很棘手!]

greg made a great start, but as written it won't work in your solution. greg 开了个好头,但正如所写的那样,它在您的解决方案中不起作用。

Here are a few changes:这里有一些变化:

Still using:还在用:

case class Salary(employee: String, amount: Double) // As before

You can either call the method on a Salary or pass it a Salary , you're trying to do both.您可以在Salary上调用该方法,也可以将它传递给Salary ,您正在尝试同时执行这两种操作。

The simplest would be to try:最简单的方法是尝试:

trait XN[M] {
    def x2: M
    def x3: M
    def x4: M
  }

implicit def salary2XN(s: Salary): XN[Salary] = {
    new XN[Salary] {
      override def x2: Salary = s.copy(amount = 2 * s.amount)
      override def x3: Salary = s.copy(amount = 3 * s.amount)
      override def x4: Salary = s.copy(amount = 4 * s.amount)
    }
  }

And now you can call each method using your signature:现在您可以使用您的签名调用每个方法:

Salary("bob", 200).x2

EDIT:编辑:

A solution using plain old extension:使用普通旧扩展的解决方案:

trait XN[M] {
    def x2: M
    def x3: M
    def x4: M
  }

  case class Salary(employee: String, amount: Double) extends XN[Salary] {
    override def x2: Salary = Salary(employee, 2 * amount)
    override def x3: Salary = Salary(employee, 3 * amount)
    override def x4: Salary = Salary(employee, 4 * amount)
  }

Which you can now call the exact same way:您现在可以用完全相同的方式调用它:

Salary("bob", 233).x2

NOTE: In the former implementation, Salary s themselves do not have the methods of the XN , rather we apply them to each Salary using an implicit conversion.注意:在前一个实现中, Salary本身没有XN的方法,而是我们使用隐式转换将它们应用于每个 Salary。 With the latter implementation, EVERY Salary has the methods for XN , we define it in the implementation of the case class itself.对于后一个实现,每个Salary都有XN的方法,我们在case class本身的实现中定义它。

Creating implicit conversion from Salary to XN could be a solution.创建从 Salary 到 XN 的隐式转换可能是一个解决方案。 This is how scala add new functionality to existing classes without extending them.这就是 Scala 向现有类添加新功能而不扩展它们的方式。

implicit def salary2XN(s: Salary): XN[Salary] = {
  new XN[Salary] {
    override def x2: Salary = Salary(s.employee, 2*s.amount)
    override def x3: Salary = Salary(s.employee, 3*s.amount)
    override def x4: Salary = Salary(s.employee, 4*s.amount)
  }
}

Since it seems that XN is a typeclass , it would be better to properly use that pattern instead of relying on a (discouraged) implicit conversion .由于XN似乎是一个类型类,因此最好正确使用该模式而不是依赖(不鼓励的)隐式转换

trait XN[M] {
  def x2(m: M): M
  def x3(m: M): M
  def x4(m: M): M
}

object XN {
  object syntax {
    implicit class XNOp[M](private val m: M) extends AnyVal {
      @inline final def x2(implicit ev: XN[M]): M = ev.x2(m)
      @inline final def x3(implicit ev: XN[M]): M = ev.x3(m)
      @inline final def x4(implicit ev: XN[M]): M = ev.x4(m)
    }
  }
}

final case class Salary(employee: String, amount: Double)
object Salary {
  implicit final val SalaryXN: XN[Salary] =
    new XN[Salary] {
      override def x2(s: Salary): Salary = s.copy(amount = s.amount * 2)
      override def x3(s: Salary): Salary = s.copy(amount = s.amount * 3)
      override def x4(s: Salary): Salary = s.copy(amount = s.amount * 4)
    }
}

Which can be used like this:可以这样使用:

import XN.syntax._

val bobSalary = Salary("Bob", 100.0)

bobSalary.x2
// res: Salary = Salary("Bob", 200.0)

You can see the code running here .您可以在此处看到运行的代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM