Reputation: 2930
I'd like to use the functions of a typeclass directly without having to refer to the implicit evidence object.
Right now I have implemented a typeclass the recommended way (at least I think so):
object Main {
import Implicits._
import Implicits.monoidInt._
def main(args: Array[String]): Unit = {
println(addTwice(1,2))
}
}
object Implicits {
implicit object monoidInt extends Monoid[Int] {
def zero: Int = 0
def append(x: Int, y: Int): Int = x + y
}
}
trait Monoid[A] {
def zero: A
def append(x: A, y: A): A
}
Instead of writing addTwice
like:
def addTwice[A](x: A, y: A)(implicit ev: Monoid[A]): A = {
ev.append(ev.append(x,y), y)
}
I'd like to write:
def addTwice[A: Monoid[A]](x: A, y: A): A = x.append(y).append(y)
Is this possible?
Upvotes: 2
Views: 304
Reputation: 8663
Yes it is possible. You need so called Ops
. It allows to add methods to objects of classes that have instance of the typeclass defined.
You define the MonoidOps
trait that has that value and instance of typeclass and then uses those in its methods, so basically it encapsulates usage of that ev
variable.
Then define a convertion from A
to MonoidOps[A]
that sets the object as well as the instance that is taken through implicit parameter.
trait Monoid[A] {
def zero: A
def append(x: A, y: A): A
}
object Implicits {
implicit object monoidInt extends Monoid[Int] {
def zero: Int = 0
def append(x: Int, y: Int): Int = x + y
}
trait MonoidOps[A] {
def instance: Monoid[A]
def self: A
def |+|(y: A): A = instance.append(self, y)
}
implicit def toMonoidOps[A](target: A)(implicit M: Monoid[A]) = new MonoidOps[A] {
val instance = M
val self = target
}
}
import Implicits._
def addTwice[A: Monoid](x: A, y: A): A = x |+| y |+| y
scala> println(addTwice(1,2))
5
MonoidOps
and toMonoidOps
could be replaced with implicit class like this:
implicit class MonoidOps[A](val self: A)(implicit M: Monoid[A]) {
def |+|(y: A): A = M.append(self, y)
}
Take look at simulacrum project that can generate the boilerplate for you.
Upvotes: 6