Michał Płachta
recognizing typeclasses
writing simple typeclasses
knowing about more complex ones
class What[+A, +Repr] {
def what[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That
}
trait TraversableLike[+A, +Repr] {
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That
}
Strings: "Vader is strong" => "strong is Vader"
Ints: 321 => 123
// classic approach - no typeclass yet!
trait Yodaizable[A] {
def yodaize : A
}
case class YodaizableString(x : String) extends Yodaizable[String] {
def yodaize = x.split(" ").reverse.mkString(" ")
}
println(YodaizableString("Vader is strong").yodaize)
trait Yodaizator[A] {
def yodaize(a : A) : A
}
Notice a subtle difference to the previous one
trait Yodaizable[A] {
def yodaize : A
}
def yodaize[A](a : A)(implicit yodaizator : Yodaizator[A]) =
yodaizator.yodaize(a)
> println(yodaize("Vader is strong"))
Error:(29, 18) could not find implicit value for
evidence parameter of type Yodaizator[String]
implicit val stringYodaizator = new Yodaizator[String] {
override def yodaize(a: String): String =
a.split(" ").reverse.mkString(" ") // cheating!
}
> println(yodaize("Vader is strong"))
strong is Vader
implicit val intYodaizator = new Yodaizator[Int] {
override def yodaize(a: Int): Int =
a.toString().reverse.toInt // cheating :(
}
> println(yodaize(321))
123
// 1. Typeclass definition
@implicitNotFound("Yodaizator instance not found for type ${A}")
trait Yodaizator[A] {
def yodaize(a : A) : A
}
// 2. Typeclass usage definition
def yodaize[A](a : A)(implicit yodaizator : Yodaizator[A]) = yodaizator.yodaize(a)
// 3. Adding classes to the typeclass (creating witness objects)
implicit val stringYodaizator = new Yodaizator[String] {
override def yodaize(a: String): String = a.split(" ").reverse.mkString(" ")
}
implicit val intYodaizator = new Yodaizator[Int] {
override def yodaize(a: Int): Int = a.toString().reverse.toInt
}
// 4. Use function with an object of any class that belongs to the typeclass
println(yodaize("Vader is strong")) // => "strong is Vader"
println(yodaize(321)) // => 123
def yodaize[A](a : A)(implicit yodaizator : Yodaizator[A]) =
yodaizator.yodaize(a)
def yodaize[A : Yodaizator](a : A) = implicitly[Yodaizator[A]].yodaize(a)
scala> case class Planet(name: String, diameter: Int, population: Int)
defined class Planet
scala>
scala> case class Planet(name: String, diameter: Int, population: Int)
defined class Planet
scala> val planets : List[Planet] = ...
scala> planets.sorted
<console>:11: error: No implicit Ordering defined for Planet.
planets.sorted
^
scala>
scala> case class Planet(name: String, diameter: Int, population: Int)
defined class Planet
scala> val planets : List[Planet] = ...
scala> planets.sorted
<console>:11: error: No implicit Ordering defined for Planet.
planets.sorted
^
scala> implicit val planetOrdering = new Ordering[Planet] {
| override def compare(x: Planet, y: Planet): Int =
| y.population - x.population
| }
planetOrdering: Ordering[Planet] = $anon$1@739ca8fe
scala> planets.sorted
res3: List[Planet] = List(
Planet(Naboo,12120,4500000), Planet(Alderaan,12500,2000000),
Planet(Bespin,118000,6000), Planet(Tatooine,10465,200))
scala>
scala> import scala.collection.generic.CanBuildFrom
scala> import scala.collection.immutable.BitSet
scala> val bs = BitSet(1,2,3)
bs: scala.collection.immutable.BitSet = BitSet(1, 2, 3)
scala>
scala> import scala.collection.generic.CanBuildFrom
scala> import scala.collection.immutable.BitSet
scala> val bs = BitSet(1,2,3)
bs: scala.collection.immutable.BitSet = BitSet(1, 2, 3)
scala> val shifted = bs map (_+1)
shifted: scala.collection.immutable.BitSet = BitSet(2, 3, 4)
scala> import scala.collection.generic.CanBuildFrom
scala> import scala.collection.immutable.BitSet
scala> val bs = BitSet(1,2,3)
bs: scala.collection.immutable.BitSet = BitSet(1, 2, 3)
scala> val shifted = bs map (_+1)
shifted: scala.collection.immutable.BitSet = BitSet(2, 3, 4)
scala> val stringified = shifted map (_+"!") // What's the result?
scala> import scala.collection.generic.CanBuildFrom
scala> import scala.collection.immutable.BitSet
scala> val bs = BitSet(1,2,3)
bs: scala.collection.immutable.BitSet = BitSet(1, 2, 3)
scala> val shifted = bs map (_+1)
shifted: scala.collection.immutable.BitSet = BitSet(2, 3, 4)
scala> val stringified = shifted map (_+"!")
stringified: scala.collection.immutable.SortedSet[String] =
TreeSet(2!, 3!, 4!)
scala> import scala.collection.generic.CanBuildFrom
scala> import scala.collection.immutable.BitSet
scala> val bs = BitSet(1,2,3)
bs: scala.collection.immutable.BitSet = BitSet(1, 2, 3)
scala> val shifted = bs map (_+1)
shifted: scala.collection.immutable.BitSet = BitSet(2, 3, 4)
scala> val stringified = shifted map (_+"!")
stringified: s.c.i.SortedSet[String] = TreeSet(2!, 3!, 4!)
scala> val cbf = implicitly[CanBuildFrom[BitSet, String, Set[String]]]
cbf: CanBuildFrom[BitSet,String,Set[String]] =
scala.collection.generic.SortedSetFactory$SortedSetCanBuildFrom
scala> import java.util._
import java.util._
scala> val arrayList = new ArrayList(Arrays.asList(1, 2, 3))
arrayList: java.util.ArrayList[Int] = [1, 2, 3]
scala> arrayList.map(_ + 1)
<console>:14: error: value map is not a member of j.u.ArrayList[Int]
arrayList.map(_ + 1)
// 1. typeclass definition
trait Functor[F[_]] {
def map[X, Y](f: X => Y): F[X] => F[Y]
}
// 1. typeclass definition
trait Functor[F[_]] {
def map[X, Y](f: X => Y): F[X] => F[Y]
}
// 2. typeclass usage definition
implicit def somethingToFunctor[F[_]: Functor, A](fa: F[A]) = new {
val witness = implicitly[Functor[F]]
final def map[B](f:A => B) : F[B] = witness.map(f)(fa)
}
// 1. typeclass definition
trait Functor[F[_]] {
def map[X, Y](f: X => Y): F[X] => F[Y]
}
// 2. typeclass usage definition
implicit def somethingToFunctor[F[_]: Functor, A](fa: F[A]) = new {
val witness = implicitly[Functor[F]]
final def map[B](f:A => B) : F[B] = witness.map(f)(fa)
}
// 3. creating witness object
implicit object ArrayListFunctor extends Functor[ArrayList] {
def map[X, Y](f: X => Y) = (xs: ArrayList[X]) => {
val ys = new ArrayList[Y]
for (i <- 0 until xs.size) ys.add(f(xs.get(i)))
ys
}
}
// 1. typeclass definition
trait Functor[F[_]] {
def map[X, Y](f: X => Y): F[X] => F[Y]
}
// 2. typeclass usage definition
implicit def somethingToFunctor[F[_]: Functor, A](fa: F[A]) = new {
val witness = implicitly[Functor[F]]
final def map[B](f:A => B) : F[B] = witness.map(f)(fa)
}
// 3. creating witness object
implicit object ArrayListFunctor extends Functor[ArrayList] {
def map[X, Y](f: X => Y) = (xs: ArrayList[X]) => {
val ys = new ArrayList[Y]
for (i <- 0 until xs.size) ys.add(f(xs.get(i)))
ys
}
}
// 4. Use function with an object of any class that belongs to the typeclass
val arrayList = new ArrayList(Arrays.asList(1, 2, 3))
println(arrayList.map(_ + 1)) // => [2, 3, 4]
Typeclasses = functional design patterns
Michał Płachta
Typeclasses in a galaxy far, far away