Created
April 29, 2011 10:28
-
-
Save julienrf/948156 to your computer and use it in GitHub Desktop.
Scala typeclass pattern
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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