Skip to content

Instantly share code, notes, and snippets.

@julienrf
Created April 29, 2011 10:28
Show Gist options
  • Save julienrf/948156 to your computer and use it in GitHub Desktop.
Save julienrf/948156 to your computer and use it in GitHub Desktop.
Scala typeclass pattern
/** Say we have a `max` function, computing the maximum of two numbers */
def max(a: Int, b: Int): Int = {
if (a < b) b
else a
}
/** It can be used like that: */
max(3, 4)
/** We want to make the max function more generic, in order to reuse it in
* different contexts.
* We choose to use a type parameter with an upper bound */
def max[T <: Ordered[T]](a: T, b: T): T = {
if (a < b) b
else a
}
/** We can easily use this generic function with a custom type, say Disk: */
case class Disk(radius: Int) extends Ordered[Disk] {
override def compare(that: Disk): Int = this.radius - that.radius
}
max(Disk(5), Disk(6))
// Unfortunately the generic max function cannot be used with Ints…
max(3, 4) // Error (Int is not a subtype of Ordered[Int])
/** Let's rewrite the max function using a typeclass pattern */
def max[T](a: T, b: T)(implicit ordered: T => Ordered[T]): T = {
if (ordered(a) < b) b // Actually the `ordered` is optional here
else a
}
/** Now we can use it with any type, given there exists an implicit conversion
* from T to Ordered[T] in the implicit scope */
case class Disk(radius: Int)
case class Rectangle(width: Int, height: Int)
implicit def orderedDisk(disk: Disk): Ordered[Disk] =
new Ordered[Disk] {
override def compare(that: Disk): Int = disk.radius - that.radius
}
// Order relation based on rectangle perimeter
implicit def orderedRectangle(rect: Rectangle): Ordered[Rectangle] =
new Ordered[Rectangle] {
override def compare(that: Rectangle): Int =
(rect.width + rect.height) - (that.width + that.height)
}
max(Disk(5), Disk(6))
max(Rectangle(1, 2), Rectangle(3, 4))
max(3, 4) // It now works with Ints (since Scala provides the required implicit def)
/** Actually the previous `max` function can be rewritten using a view bound: */
def max[T <% Ordered[T]](a: T, b: T): T = {
if (a < b) b
else a
}
/** It could also have been written with a context bound: */
import math.Ordered._ // Load an implicit conversion from Ordering[T] to Ordered[T]
def max[T : Ordering](a: T, b: T): T = {
if (a < b) b
else a
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment