ADTs & Typeclasses

in Scala


Jan Schulte

About me

Freelance Scala consultant

Currently Scala lead @ Douglas

Scala UG Düsseldorf (@scaladus)

Idris UG Düsseldorf (@idrisdus)

Algebraic Data Types

In computer programming, more so functional programming and type theory, an algebraic data type is a kind of composite type, i.e., a type formed by combining other types.

Source Wikipedia

Sum Type

Describes + types or logical or types

Thinking in ***is a*** relationships

A TrafficLight is Green or Yellow or Red

A Day is
Monday or Tuesday or Wednesday or
Thursday or Friday or Saturday or
Sunday

So how do we represent that in Scala?

Sneak peek Scala 3

(aka Dotty)

Product Type

Describes types or logical and types

Thinking in ***has a*** relationships

A Person has a Name and an Age

A Point has an X-coordinate and a Y-coordinate

And now in Scala...

Product type 2.0 aka Records

A Person has a Name and an Age

Composition

A result of a computation ***is*** empty ***or*** it ***has*** just a value

A result of a computation ***is*** either a String ***or*** a Double

Recursive types

Source Screenshot www.douglas.es (2017-07-05)

A page menu ***is*** either a menu item that ***has*** a title

***or*** a menu category that ***has*** a title and 1..* submenus

Pro tips

#1 - Wrap Sum type in companion object

#2 - Abstract similar Product types

#3 - Parameterize type

#3 - Parameterize type

Typeclasses

ADTs: Sum, Product, Recursive...

All nice and shiny...

...but how do we add functionality to our ADTs?

Expression problem

Example

Calculate the area of a shape

Adding subtypes is easy

Adding functions is difficult

GoF Visitor Pattern! Don't you dare!

Typeclasses to the rescue

Concept from FP languages (Haskell)

Separate functionality and data types

Area Typeclass

Typeclasses - Simulacrum

Compiler plugin by Michael Pilquist

Reduce required typeclass boilerplate

                
import simulacrum._

@typeclass trait JsonSerialisable[A] {
@op("json") def serialise(elem:A): String
}

implicit val intSerialisable = new JsonSerialisable[Int] {
  override def serialise(elem:Int) = "{\"elem\":"+ elem + "}"
}

import JsonSerialisable.ops._
1.json // prints {"elem":1}
                

Show typeclass

Get a textual representation of an object

a.k.a toString

Do you know how toString() is used in your project?

  • Testing
  • Person(Name(Jan),Age(32))
  • Logging
  • Jan[age=32]
  • Json Serialisation

Do you know how toString() is used in your project?

  • Output Css classes (hmmkay)
  • Create Javascript tracking code (seriously?)
  • Create a checksum (dafuq???)

Typelevel Cats library

                
/**
* A type class to provide textual representation
*/
trait Show[A] {
    def show(f: A): String
}

Source Cats

Opinionated guideline

#1 - Use typeclasses

...for generic functionality not specific to your data type

                
@typeclass trait Json[E] {
    @op("json") def toJson(elem: E): String
}

#2 - Use structural recursion

...for basic data manipulation

                
trait Shape {
    def map(f: A):Shape = this match {
        case class Rectangle(w,h) => Rectangle(f(w),f(h))
        case class Circle(r) => Circle(f(r))
    }
}

#3 - Use a helper object

...for one-time functionality specific to your data type

                
object Calculator {

  def deriveEasterDiscount(price: Price): Discount = ???

}

Take aways

  • ADTs are simple yet powerful.
  • So go for it! Model your data as ADTs...
  • ...and be dumb! Think in is-a and has-a relationships.
  • ...but keep your ADTs clean...
  • ...and use Typeclasses to add functionality!

Thank you!

@janschultecom

github.com/janschultecom

Presentation Link: janschulte.com/2017-07-06-ADTs-and-Typeclasses