Introduction to Typeclasses
2015-06-05 | Egemen Kalyoncu | Amsterdam
Agenda
● What is typeclass?
● Example
● Category Theory
● Example
● Higher Kinded Types
● Example
● Pros/Cons
Type Classes
“Typeclasses define a set of functions that can
have different implementations depending on the
type of data they are given” Real World Haskell
“In computer science, a type class is a type
system construct that supports ad-hoc
polymorphism.” Wikipedia
Type Classes
In Haskell, typeclass is the language
feature. Every class is a typeclass.
In Scala, typeclass is a pattern.
What isn’t Typeclass?
● It’s not only about FP. You can use it in OOP
as well.
● It’s not only about category theory.
Typeclass
Typeclass
● scala.math.Ordering
● scala.math.Numeric
● scalaz._
● spray.json._
Polymorphism
● Subtype polymorphism
● Parametric polymorphism (generics)
● Ad-hoc polymorphism (decoupling)
Serialization Example
class Ad(id: Int, title: String)
class User(id: Int, name: String)
Let’s implement serialization of these
classes.
Serialization Example (subtyping)
Serialization Example (subtyping)
trait Serializable { def serialize: String }
def doSerialize(in: Serializable)= in.serialize
class Ad extends Serializable {
def serialize: String = "serialized ad"
}
class User extends Serializable {
def serialize: String = "serialized user"
}
Serialization Example (typeclass)
trait Serializable[A] { def serialize(a: A): String }
def doSerialize[A](a: A, ser: Serializable[A]) =
ser.serialize(a)
Serialization Example (typeclass)
trait Serializable[A] { def serialize(a: A): String }
def doSerialize[A](a: A)(implicit ser: Serializable[A]) =
ser.serialize(a)
Serialization Example (typeclass)
object Serializable {
implicit val adSerializable: Serializable[Ad] =
new Serializable[Ad] {
def serialize(ad: Ad) = "serialized ad"
}
implicit val userSerializable: Serializable[User] =
new Serializable[User] {
def serialize(user: User) = "serialized user"
}
}
Serialization Example (typeclass)
scala> doSerialize(new Ad(1, "title1"))
res0: String = serialized ad
scala> doSerialize(new User(1, "user1"))
res1: String = serialized user
scala> doSerialize("bla")
<console>:11: error: could not find implicit value for parameter ser:
Serializable[String]
doSerialize("bla")
Category Theory
● Branch of mathematics
● Study of concepts(collection of objects)
and arrows(morphism)
● Functional design patterns in
programming
● Categories have properties and laws
● Categories can be types, cats, cars, …
Category Theory
arrow : function, map, morphism,
transformation, operator
Category Theory
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Category Theory
● Abstraction is useful but...
● Don’t forget social aspect of
programming
● Check if everybody is comfortable with
these advanced techniques.
How to sum
def sum(l: List[Int]): Int
How to sum
def sum(l: List[Int]): Int = l.reduce(_ + _)
How to sum
It looks like working. Except empty list.
How to sum
What about List[Double]?
How to sum
def sumDouble(l: List[Double]): Double = l.reduce(_ + _)
How to sum
def sumNumeric[A](l: List[A])(implicit A: Numeric[A]): A =
l.reduce(A.plus)
How to sum
What about List[String] or List[A]?
Let’s make it generic!
We need a typeclass to sum any type of A
How to sum
trait Addable[A] {
def plus(x: A, y: A): A
}
How to sum
object Addable {
implicit def numericAddable[A](implicit A: Numeric[A]): Addable[A] =
new Addable[A] {
def plus(x: A, y: A): A = A.plus(x, y)
}
implicit val stringAddable: Addable[String] =
new Addable[String] {
def plus(x: String, y: String): String = x + y
}
}
How to sum
def sumGeneric[A](l: List[A])(implicit A: Addable[A]): A =
l.reduce(A.plus)
How to sum
trait AddableWithZero[A] extends Addable[A] { def zero: A }
object AddableWithZero {
implicit def numericAddableZero[A](implicit A: Numeric[A]): AddableWithZero[A] =
new AddableWithZero[A] {
def plus(x: A, y: A): A = A.plus(x, y)
def zero: A = A.zero
}
implicit val stringAddableZero: AddableWithZero[String] =
new AddableWithZero[String] {
def plus(x: String, y: String): String = x + y
def zero: String = ""
}
}
How to sum
def sumGeneric[A](l: List[A])(implicit A: AddableWithZero[A]): A
= l.foldLeft(A.zero)(A.plus)
Semigroup and Monoid
● Abstract algebra
● Addable => Semigroup
● AddableWithZero => Monoid
Semigroup
trait Semigroup[A] {
def append(a: A, b: A): A
}
● Simple typeclass
● Law: Associativity
o append(a1, append(a2, a3) == append(append(a1, a2), a3)
Monoid
trait Monoid[A] extends Semigroup[A] {
def zero : A
}
● Identity element on top of semigroup
● Laws: Identity law
o zero append a == a
o a append zero == a
How to sum with Scalaz
import scalaz.Monoid
def sumGeneric[A](l: List[A])(implicit A: Monoid[A]): A =
l.foldLeft(A.zero)((x, y) => A.append(x, y))
Higher Kinded Types
● HKT gives us the ability to
generalize across type constructors
● Reduces code duplication
● parser combinators, DSL,
comprehensions are written using HKT
Higher Kinded Types
Higher Kinded Types
import scalaz.Monoid
def sumGeneric[A](l: List[A])(implicit A: Monoid[A]): A =
l.foldLeft(A.zero)((x, y) => A.append(x, y))
What if we want to sum over Vector, Tree or Map?
Higher Kinded Types
trait Foldable[F[_]] {
def fold[M](fa: F[M])(implicit M: Monoid[M]): M
}
object Foldable {
implicit val listFoldable: Foldable[List] =
new Foldable[List] {
def fold[M](fa: List[M])(implicit M: Monoid[M]): M =
fa.foldLeft(M.zero)((acc, elem) => M.append(acc, elem))
}
}
Higher Kinded Types
def sumGeneric2[F[_], A](fa: F[A])(implicit F: Foldable[F], A: Monoid[A]): A =
F.fold(fa)
Pros
● Unlike subtype polymorphism, type classes
work at compile time.
● Decouple functionality from types
● Extensible
Cons
● code complexity?
● performance penalty?
http://stackoverflow.com/questions/9327465/what-is-the-performance-impact-of-using-the-type-class-pattern-in-scala
References
● http://eed3si9n.com/learning-scalaz/polymorphism.html
● https://speakerdeck.com/marakana/ne-scala-2012-the-typeclass-pattern-an-alternative-to-inheritance
● http://typelevel.org/blog/2013/10/13/towards-scalaz-1.html
● http://typelevel.org/blog/2013/12/15/towards-scalaz-2.html
● https://www.haskell.org/tutorial/classes.html
● Scala in Depth
● Programming Scala 2nd Edition
● Generics of a Higher Kind - Adriaan Moors, Frank Piessens, and Martin Odersky

Typeclasses

  • 1.
    Introduction to Typeclasses 2015-06-05| Egemen Kalyoncu | Amsterdam
  • 2.
    Agenda ● What istypeclass? ● Example ● Category Theory ● Example ● Higher Kinded Types ● Example ● Pros/Cons
  • 3.
    Type Classes “Typeclasses definea set of functions that can have different implementations depending on the type of data they are given” Real World Haskell “In computer science, a type class is a type system construct that supports ad-hoc polymorphism.” Wikipedia
  • 4.
    Type Classes In Haskell,typeclass is the language feature. Every class is a typeclass. In Scala, typeclass is a pattern.
  • 5.
    What isn’t Typeclass? ●It’s not only about FP. You can use it in OOP as well. ● It’s not only about category theory.
  • 6.
  • 7.
  • 8.
    Polymorphism ● Subtype polymorphism ●Parametric polymorphism (generics) ● Ad-hoc polymorphism (decoupling)
  • 9.
    Serialization Example class Ad(id:Int, title: String) class User(id: Int, name: String) Let’s implement serialization of these classes.
  • 10.
  • 11.
    Serialization Example (subtyping) traitSerializable { def serialize: String } def doSerialize(in: Serializable)= in.serialize class Ad extends Serializable { def serialize: String = "serialized ad" } class User extends Serializable { def serialize: String = "serialized user" }
  • 12.
    Serialization Example (typeclass) traitSerializable[A] { def serialize(a: A): String } def doSerialize[A](a: A, ser: Serializable[A]) = ser.serialize(a)
  • 13.
    Serialization Example (typeclass) traitSerializable[A] { def serialize(a: A): String } def doSerialize[A](a: A)(implicit ser: Serializable[A]) = ser.serialize(a)
  • 14.
    Serialization Example (typeclass) objectSerializable { implicit val adSerializable: Serializable[Ad] = new Serializable[Ad] { def serialize(ad: Ad) = "serialized ad" } implicit val userSerializable: Serializable[User] = new Serializable[User] { def serialize(user: User) = "serialized user" } }
  • 15.
    Serialization Example (typeclass) scala>doSerialize(new Ad(1, "title1")) res0: String = serialized ad scala> doSerialize(new User(1, "user1")) res1: String = serialized user scala> doSerialize("bla") <console>:11: error: could not find implicit value for parameter ser: Serializable[String] doSerialize("bla")
  • 16.
    Category Theory ● Branchof mathematics ● Study of concepts(collection of objects) and arrows(morphism) ● Functional design patterns in programming ● Categories have properties and laws ● Categories can be types, cats, cars, …
  • 17.
    Category Theory arrow :function, map, morphism, transformation, operator
  • 18.
    Category Theory trait Functor[F[_]]{ def map[A, B](fa: F[A])(f: A => B): F[B] }
  • 19.
    Category Theory ● Abstractionis useful but... ● Don’t forget social aspect of programming ● Check if everybody is comfortable with these advanced techniques.
  • 20.
    How to sum defsum(l: List[Int]): Int
  • 21.
    How to sum defsum(l: List[Int]): Int = l.reduce(_ + _)
  • 22.
    How to sum Itlooks like working. Except empty list.
  • 23.
    How to sum Whatabout List[Double]?
  • 24.
    How to sum defsumDouble(l: List[Double]): Double = l.reduce(_ + _)
  • 25.
    How to sum defsumNumeric[A](l: List[A])(implicit A: Numeric[A]): A = l.reduce(A.plus)
  • 26.
    How to sum Whatabout List[String] or List[A]?
  • 27.
    Let’s make itgeneric! We need a typeclass to sum any type of A
  • 28.
    How to sum traitAddable[A] { def plus(x: A, y: A): A }
  • 29.
    How to sum objectAddable { implicit def numericAddable[A](implicit A: Numeric[A]): Addable[A] = new Addable[A] { def plus(x: A, y: A): A = A.plus(x, y) } implicit val stringAddable: Addable[String] = new Addable[String] { def plus(x: String, y: String): String = x + y } }
  • 30.
    How to sum defsumGeneric[A](l: List[A])(implicit A: Addable[A]): A = l.reduce(A.plus)
  • 31.
    How to sum traitAddableWithZero[A] extends Addable[A] { def zero: A } object AddableWithZero { implicit def numericAddableZero[A](implicit A: Numeric[A]): AddableWithZero[A] = new AddableWithZero[A] { def plus(x: A, y: A): A = A.plus(x, y) def zero: A = A.zero } implicit val stringAddableZero: AddableWithZero[String] = new AddableWithZero[String] { def plus(x: String, y: String): String = x + y def zero: String = "" } }
  • 32.
    How to sum defsumGeneric[A](l: List[A])(implicit A: AddableWithZero[A]): A = l.foldLeft(A.zero)(A.plus)
  • 33.
    Semigroup and Monoid ●Abstract algebra ● Addable => Semigroup ● AddableWithZero => Monoid
  • 34.
    Semigroup trait Semigroup[A] { defappend(a: A, b: A): A } ● Simple typeclass ● Law: Associativity o append(a1, append(a2, a3) == append(append(a1, a2), a3)
  • 35.
    Monoid trait Monoid[A] extendsSemigroup[A] { def zero : A } ● Identity element on top of semigroup ● Laws: Identity law o zero append a == a o a append zero == a
  • 36.
    How to sumwith Scalaz import scalaz.Monoid def sumGeneric[A](l: List[A])(implicit A: Monoid[A]): A = l.foldLeft(A.zero)((x, y) => A.append(x, y))
  • 37.
    Higher Kinded Types ●HKT gives us the ability to generalize across type constructors ● Reduces code duplication ● parser combinators, DSL, comprehensions are written using HKT
  • 38.
  • 39.
    Higher Kinded Types importscalaz.Monoid def sumGeneric[A](l: List[A])(implicit A: Monoid[A]): A = l.foldLeft(A.zero)((x, y) => A.append(x, y)) What if we want to sum over Vector, Tree or Map?
  • 40.
    Higher Kinded Types traitFoldable[F[_]] { def fold[M](fa: F[M])(implicit M: Monoid[M]): M } object Foldable { implicit val listFoldable: Foldable[List] = new Foldable[List] { def fold[M](fa: List[M])(implicit M: Monoid[M]): M = fa.foldLeft(M.zero)((acc, elem) => M.append(acc, elem)) } }
  • 41.
    Higher Kinded Types defsumGeneric2[F[_], A](fa: F[A])(implicit F: Foldable[F], A: Monoid[A]): A = F.fold(fa)
  • 42.
    Pros ● Unlike subtypepolymorphism, type classes work at compile time. ● Decouple functionality from types ● Extensible
  • 43.
    Cons ● code complexity? ●performance penalty? http://stackoverflow.com/questions/9327465/what-is-the-performance-impact-of-using-the-type-class-pattern-in-scala
  • 44.
    References ● http://eed3si9n.com/learning-scalaz/polymorphism.html ● https://speakerdeck.com/marakana/ne-scala-2012-the-typeclass-pattern-an-alternative-to-inheritance ●http://typelevel.org/blog/2013/10/13/towards-scalaz-1.html ● http://typelevel.org/blog/2013/12/15/towards-scalaz-2.html ● https://www.haskell.org/tutorial/classes.html ● Scala in Depth ● Programming Scala 2nd Edition ● Generics of a Higher Kind - Adriaan Moors, Frank Piessens, and Martin Odersky

Editor's Notes