Type classes and implicit conversions in Scala
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
// this is a type class | |
trait Eq [A] { | |
def === (x: A)(y: A): Boolean | |
def =/= (x: A)(y: A): Boolean | |
} | |
// it is a reasonable translation of this Haskell code (Wiki) | |
// class Eq a where | |
// (==) :: a -> a -> Bool | |
// (/=) :: a -> a -> Bool | |
object Implicits { | |
// this is a member of the type class | |
implicit object IntEq extends Eq[Int] { | |
def === (x: Int)(y: Int): Boolean = x == y | |
def =/= (x: Int)(y: Int): Boolean = x != y | |
} | |
// this is another member of the type class | |
implicit object StringEq extends Eq[String] { | |
def === (x: String)(y: String): Boolean = x == y | |
def =/= (x: String)(y: String): Boolean = x != y | |
} | |
// ... | |
} | |
// this is how to use the type class | |
@scala.annotation.tailrec | |
def elem [A : Eq](y: A, ys: List[A]): Boolean = ys match { | |
case List() => false | |
case x :: xs => | |
implicitly[Eq[A]].===(x)(y) || elem (y, xs) | |
} | |
// is this a reasonable translation of this Haskell code? | |
// elem :: Eq a => a -> [a] -> Bool | |
// elem y [] = False | |
// elem y (x:xs) = (x == y) || elem y xs | |
// [T : Eq] requires an implicit Eq[T] in scope | |
import Implicits._ | |
// examples | |
elem (3, List(3, 4, 5, 6)) | |
elem (3, List(0, 4, 5, 6)) | |
elem ("hey", List("hop", "hup", "hoo")) | |
elem ("hup", List("hop", "hup", "hoo")) | |
// add a new one and use it | |
implicit object DoubleEq extends Eq[Double] { | |
def === (x: Double)(y: Double): Boolean = x == y | |
def =/= (x: Double)(y: Double): Boolean = x != y | |
} | |
elem (3.3, List(3.3, 4.3, 5.3, 6.3)) | |
elem (3.3, List(0.3, 4.3, 5.3, 6.3)) | |
// abandon hope all ye who enter here | |
// this is a slightly different type class | |
trait SimpleEq [A] { | |
def === (y: A): Boolean | |
def =/= (y: A): Boolean | |
} | |
// this is an implicit conversion for Ints | |
implicit def int2SimpleEq (x: Int): SimpleEq[Int] = new SimpleEq[Int] { | |
def === (y: Int): Boolean = x == y | |
def =/= (y: Int): Boolean = x != y | |
} | |
// this is an implicit conversion for Strings | |
implicit def string2SimpleEq (x: String): SimpleEq[String] = new SimpleEq[String] { | |
def === (y: String): Boolean = x == y | |
def =/= (y: String): Boolean = x != y | |
} | |
// this is an implicit conversion for arbitrary tuples | |
implicit def tuple2SimpleEq [A, B] (x: (A, B)): SimpleEq[(A, B)] = new SimpleEq[(A, B)] { | |
def === (y: (A, B)): Boolean = x == y | |
def =/= (y: (A, B)): Boolean = x != y | |
} | |
// this is an implicit conversion for any arbitrary type T | |
implicit def t2SimpleEq [T] (x: T): SimpleEq[T] = new SimpleEq[T] { | |
def === (y: T): Boolean = x == y | |
def =/= (y: T): Boolean = x != y | |
} | |
// works for any arbitrary types | |
false =/= true | |
false === true | |
((x: Int) => x*2) =/= ((x: Int) => x*2) | |
((x: Int) => x*2) === ((x: Int) => x*2) | |
// this is how to use the type class | |
@scala.annotation.tailrec | |
def simpleElem2 [A](y: A, ys: List[A]): Boolean = ys match { | |
case List() => false | |
case x :: xs => | |
x.===(y) || simpleElem2 (y, xs) | |
} | |
simpleElem2 (("hey", 3), List(("hey", 5), ("hoo", 4), ("hup", 5))) | |
simpleElem2 (("hey", 3), List(("hey", 3), ("hoo", 4), ("hup", 5))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment