Skip to content

Instantly share code, notes, and snippets.

@awwsmm
Created June 25, 2020 15:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save awwsmm/deafa5556bfb2ef8c1d89118f3223070 to your computer and use it in GitHub Desktop.
Save awwsmm/deafa5556bfb2ef8c1d89118f3223070 to your computer and use it in GitHub Desktop.
Type classes and implicit conversions in Scala
// 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