Reputation: 3956
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:
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
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
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?
Upvotes: 1
Views: 117
Reputation: 724
[Use Accepted solution, implicits are tricky!]
greg made a great start, but as written it won't work in your solution.
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.
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. With the latter implementation, EVERY Salary
has the methods for XN
, we define it in the implementation of the case class
itself.
Upvotes: 3
Reputation: 22840
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.
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.
Upvotes: 2
Reputation: 1160
Creating implicit conversion from Salary to XN could be a solution. This is how scala add new functionality to existing classes without extending them.
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)
}
}
Upvotes: 2