Functional refactoring

James Earl Douglas

May 28, 2016

Motivation

How can I learn to convert imperative code to functional code?

Functional refactoring

...functionally!

Refactorings in this talk

Dependency injection → reader monad

Dependency injection is:

Dependency injection → reader monad

Example: Cylindrical volume

def area(pi: Double, r: Int): Double = pi * r * r

Dependency injection → reader monad

Example: Cylindrical volume

def volume(pi: Double, r: Int, h: Int): Double = {
  val a = area(pi, r)
  val v = a * h
  v
}

Dependency injection → reader monad

Step 0: Our reader monad

case class Reader[E,A](run: E => A) {

  def flatMap[B](f: A => Reader[E,B]): Reader[E,B] =
    Reader[E,B] { e => f(run(e)).run(e) }

  def map[B](f: A => B): Reader[E,B] =
    Reader[E,B] { e => f(run(e)) }

}

Dependency injection → reader monad

Step 1: Extract Pi as a dependency

// def area(pi: Double, r: Int): Double = pi * r * r
def areaR(r: Int): Reader[Double,Double] =
  Reader { pi => pi * r * r }

Dependency injection → reader monad

Step 1: Extract Pi as a dependency

// def volume(pi: Double, r: Int, h: Int): Double = ...
def volumeR(r: Int, h: Int): Reader[Double,Double] =
  areaR(r) map { a => a * h }

Dependency injection → reader monad

Usage: Pi injection

val vR: Double = volumeR(6, 7) run 3.14

Dependency injection → reader monad

Step 2: Extract radius as a dependency

// def areaR(r: Int): Reader[Double,Double] =
val areaRR: Reader[Int,Reader[Double,Double]] =
  Reader { r =>
    Reader { pi => pi * r * r }
  }

Dependency injection → reader monad

Step 2: Extract radius as a dependency

// def volumeR(r: Int, h: Int): Reader[Double,Double] = ...
def volumeRR(h: Int): Reader[Int,Reader[Double,Double]] =
  areaRR map { areaR =>
    areaR map { a => a * h }
  }

Dependency injection → reader monad

Usage: Radius and Pi injection

val vRR: Double = volumeRR(7) run 6 run 3.14

Dependency injection → reader monad

Questions

Exception → sum type

Exceptions are:

Exception → sum type

Example: Integer division

def div(x: Int, y: Int): Int = x / y

Exception → sum type

Example: Division by zero

val quotient: Int =
  try {
    div(42, 0)
  } catch {
    case e: ArithmeticException => -999
  }

Exception → sum type

Step 0: Type disjunction

sealed trait Either[A,B]
case class Left[A,B](x: A) extends Either[A,B]
case class Right[A,B](x: B) extends Either[A,B]

Exception → sum type

Step 1: Return either a quotient or an exception

// def div(x: Int, y: Int): Int = x / y
def divE(x: Int, y: Int): Either[Exception, Int] =
  try {
    Right(x / y)
  } catch {
    case e: Exception => Left(e)
  }

Exception → sum type

Usage: Division by zero

// val quotient: Int = ...
val quotientE: Either[Exception,Int] = divE(42, 0)

Exception → sum type

Step 2: Make it composable

implicit class RightBiasedEither[A,B](e: Either[A,B]) {

  def flatMap[C](f: B => Either[A,C]): Either[A,C] =
    e match {
      case Left(a) => Left(a)
      case Right(b) => f(b)
    }

  def map[C](f: B => C): Either[A,C] =
    flatMap { b => Right(f(b)) }

}

Exception → sum type

Usage: Chained divisions

val quotientE2: Either[Exception,Int] =
  for {
    a <- divE(42, 7)
    b <- divE(a, 0)
    c <- divE(b, 2)
  } yield c

Exception → sum type

Questions

Mutable variable → state monad

Mutable variables are:

Mutable variable → state monad

Example: Six equals forty-two

var x = 6

println(s"x = ${x}")
// x = 6

x = x * 7

println(s"x = ${x}")
// x = 42

Mutable variable → state monad

Step 0: Our state monad

case class State[A,S](run: S => (A,S)) {

  def flatMap[B](f: A => State[B,S]): State[B,S] =
    State { s =>
      val (a,s2) = run(s)
      f(a).run(s2)
    }

}

Mutable variable → state monad

Step 1: Some handy extras

implicit class StateExtras[A,S](s: State[A,S]) {

  def andThen[B](x: State[B,S]): State[B,S] =
    s.flatMap { _ => x }

  def map[B](f: A => B): State[B,S] =
    s.flatMap { a =>
      State { s => (f(a),s) }
    }

}

Mutable variable → state monad

Step 2: Replace var with State

// var x = 6
val six: State[Unit,Int] =
  State { _ => ((),6) }

Mutable variable → state monad

Step 3: Replace reference with State

// println(s"x = ${x}")
val print: State[Unit,Int] =
  State { x => (println(s"x = ${x}"),x) }

Mutable variable → state monad

Step 4: Replace mutation with State

// x = x * 7
val times7: State[Unit,Int] =
  State { x => ((),x*7) }

Mutable variable → state monad

Step 5: Combine as needed

// var x = 6
// println(s"x = ${x}") // x = 6
// x = x * 7
// println(s"x = ${x}") // x = 42
val sixBy7: State[Unit,Int] =
  six andThen print andThen times7 andThen print

Mutable variable → state monad

Usage: Throwaway initial state

sixBy7 run -999
// x = 6
// x = 42

Mutable variable → state monad

Questions

Loop → fold

Loops are:

Loop → fold

Example: Sum of a list of numbers

val xs = List(1,2,3,4)
var i = 0
var sum = 0
while (i < xs.length) {
  val x = xs(i)
  sum = sum + x
  i = i + 1
}

Loop → fold

Step 0: Left fold

scala> :t xs.foldLeft[B] _
B => (((B, Int) => B) => B)

We'll need:

Loop → fold

Step 1: Finalize the initial value

// var sum = 0
val sumInit = 0

Loop → fold

Step 2: Extract the loop body

// while (i < xs.length) {
//   val x = xs(i)
//   sum = sum + x
//   i = i + 1
// }
val sumOp: (Int,Int) => Int = { (sum, x) => sum + x }

Loop → fold

Usage: Fold over the list

val foldSum: Int = xs.foldLeft(sumInit)(sumOp)

Loop → fold

Questions

Mutator method → deep copy

Mutable variables are:

Mutator method → deep copy

Example: A mutable employee

class MutableEmployee(var name: String, var title: String) {

  def setName(name: String): Unit = {
    this.name = name
  }

  def setTitle(title: String): Unit = {
    this.title = title
  }

}

Mutator method → deep copy

Example: Changing state over time

val employee0 = new MutableEmployee("George Michael", "Employee")

println(s"employee0: ${employee0.name}, ${employee0.title}")
// employee0: George Michael, Employee

employee0.setTitle("Mr. Manager")

println(s"employee0: ${employee0.name}, ${employee0.title}")
// employee0: George Michael, Mr. Manager

Mutator method → deep copy

Step 1: Replace var with val

// class MutableEmployee(var name: String, var title: String) {
class ImmutableEmployee(val name: String, val title: String) {

Mutator method → deep copy

Step 2: Return a deep copy

  // def setName(name: String): Unit = ...
  def setName(name: String): ImmutableEmployee =
    new ImmutableEmployee(name, title)

  // def setTitle(title: String): Unit = ...
  def setTitle(title: String): ImmutableEmployee =
    new ImmutableEmployee(name, title)

}

This is a functional take on the builder pattern.

Mutator method → deep copy

Usage: Immutable promotion

val employee1 = new ImmutableEmployee("George Michael", "Employee")
val employee2 = employee1.setTitle("Mr. Manager")

println(s"employee1: ${employee1.name}, ${employee1.title}")
// employee1: George Michael, Employee

println(s"employee2: ${employee2.name}, ${employee2.title}")
// employee2: George Michael, Mr. Manager

Mutator method → deep copy

Questions

Conclusion

How can I learn to convert imperative code to functional code?

earldouglas.com/posts/itof.html