A functional Journey

by Rewati Raman
HTML5 Icon

A Gentle Intro To Algebraic Data Types

01 Mar 2017

Algebraic data types(ADTs) are a functional programming concept. This should not be confused with Abstract data types. For example Tree is a abstract data type but not algebraic data type. Algebraic data type is data representation using defined algebraic functions or expressions. Definition of algebraic data type can simply also stated as data representation using Ands and Ors. For example: - a shape is a rectangle or a circle - a rectangle has a width and a height - a circle has a radius

“And” types such as rectangle and circle are called products and “Or” types such as shape are called coproducts. In Scala we typically represent products using case classes and coproducts using sealed traits.

  scala> sealed trait Shape
  defined trait Shape

  scala>   final case class Rectangle(width: Double, height: Double) extends Shape
  defined class Rectangle

  scala>   final case class Circle(radius: Double) extends Shape
  defined class Circle

  scala>   val rect: Shape = Rectangle(1, 2)
  rect: Shape = Rectangle(1.0,2.0)

  scala>   val circ: Shape = Circle(1)
  circ: Shape = Circle(1.0)

Shape are coproducts defined using trait. Rectangle and Circle are Product defined through case class. ADTs are completely type safe. The compiler has complete knowledge of the algebras we define, so it can help us write complete, correctly typed methods involving our types. Algebra in ADTs mean symbols we define, such as rectangle and circle and the rules for manipulating those symbols, encoded as methods.

  scala> def area(shape: Shape): Double =
     |     shape match {
     |       case Rectangle(w, h) => w * h
     |       case Circle(r)       => math.Pi * r * r
     |     }
  area: (shape: Shape)Double

  scala>   area(rect)
  res0: Double = 2.0

The above can also be achieved in another way. Scala standard library provides generic products in the form of Tuples and a generic coproduct in the form of Either.

  scala> type Rectangle2 = (Double, Double)
  defined type alias Rectangle2

  scala>   type Circle2    = Double
  defined type alias Circle2

  scala>   type Shape2     = Either[Rectangle2, Circle2]
  defined type alias Shape2

  scala>   val rect2: Shape2 = Left((3.0, 4.0))
  rect2: Shape2 = Left((3.0,4.0))

  scala>   val circ2: Shape2 = Right(1.0)
  circ2: Shape2 = Right(1.0)

  scala> def area2(shape: Shape2): Double =
     |   shape match {
     |     case Left((w, h)) => w * h
     |     case Right(r)     => math.Pi * r * r
     |   }
  area2: (shape: Shape2)Double

  scala> area2(rect2)
  res2: Double = 12.0