Being Purely Functional
in
an Impure World

Noel Markham (47 Degrees)

Hello

  • Noel Markham

Hello

Started life as a Java developer

Writing Scala since 2012:

  • Games
  • Startups
  • TV
  • ... and more

Being Purely Functional in an Impure World

Agenda

  • Parametric Polymorphism
  • Immutability
  • Referential Transparency
  • New FP Techniques

Parametric Polymorphism

Quiz time

What does this function do?

def reverse(s: List[String]): List[String]
scala> reverse(List("a", "b", "c"))
res0: List[String] = List(c, b, a)
scala> reverse(List("Test", "Input"))
res1: List[String] = List(Hello, World)

???

Parametric Polymorphism

Quiz time

What about this function?

def identity(i: Int): Int
scala> identity(0)
res2: Int = 0
scala> identity(-6)
res3: Int = 0

Can we do better?

Parametric Polymorphism

Also known as generics, type parameters

Revisiting our identity function

def identity[A](a: A): A = a
scala> identity(0)
res4: Int = 0
scala> identity(-6)
res5: Int = -6

How many ways can this be implemented?

Parametric Polymorphism

Also known as generics, type parameters

Revisiting our reverse function

def reverse[A](as: List[A]): List[A] = {
  as match {
    case Nil => Nil 				      
    case head :: tail => reverse(tail) :+ head
  }
}
scala> reverse(List("Test", "Input"))
res6: List[String] = List(Input, Test)

How many ways can this be implemented?

Parametric Polymorphism

What value does this give us?

Counterintuitive

Simply by adding a constraint we've set our code free

More general

One implementation regardless of type

We know nothing about the input

We cannot change or manipulate it in any way

Types are documentation!

We can infer some behaviour simply from type signatures

Parametric Polymorphism in an Impure World

Using type parameters is better than unit testing

If there's only one way to write a particular function, there's often very few ways to test it

Immutability

How does this differ from other languages?

  • In Java, for example, you specify a variable to be final
  • In C# it is readonly

Immutability is switched off by default

Immutability

In functional languages...

  • Scala: val as opposed to var
  • F#: default, use the keyword ref for mutations
  • Haskell: Not possible to mutate a variable!

Immutability is switched on by default

A small hurdle for the programmer to turn it on

Immutability

What's the point?

  • Immutable values cannot change
  • State can be shared across threads
  • State can be shared across arbitrary pieces of code

Immutability

Sharing state

val xs = List(0, 1, 2)
val ys = List(3, 4, 5)

By VineetKumar at English Wikipedia, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=3594533

Immutability

Sharing state

val zs = xs ++ ys

By VineetKumar at English Wikipedia, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=3594592

Immutability

Sharing state


By VineetKumar at English Wikipedia, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=3594620

Immutability

From scala.collection.immutable.List:

override def drop(n: Int): List[A] = {
  var these = this
  var count = n
  while (!these.isEmpty && count > 0) {
    these = these.tail
    count -= 1
  }
  these
}

Mutable code! In the standard library too!

What gives?

Premature optimization is the root of all evil

Immutability in an Impure World

It is very easy to step outside of the boundaries of immutability

Lean on your compiler and other tools as much as possible

  • Turn on compiler warnings
  • ... and make compiler warnings errors
  • Set defaults in your IDE
  • Find build plugins for code style to alert on mutable uses

Referential Transparency

What is it?

Simply put, calling a function multiple times with the same inputs will always result in the same output

The compiler can defer function execution to a time of its choosing

The compiler can replace calls with the same arguments as the result of a single call

Referential Transparency

This is not unique to functional programming

Some functional languages enforce referential transparency, most do not

In impure languages, you need to think of referential transparency as a mindset

Referential Transparency

Side effects

What are side effects?

A non-exhaustive list:

  • Printing to the screen
  • Interacting with a database
  • Writing to a file
  • Asking the operating system for the current time

Referential Transparency

Side effects

  • Aim for your functions to do one thing, and one thing only
  • Be clear when your you need some side effects in your code
  • If your language lets you do this with types, use it!

Examples:

  • Future in Scala
  • IO in Haskell

Note, Scala's Future is very different to Java's Future

Referential Transparency in an Impure World

Again, it is very easy to step outside of the boundaries of immutability

Establish conventions within your team

Lean on your compiler if you can

This is also applicable to languages like Scala

Functional Programming Techniques

Typeclasses

Defined as ad-hoc polymorphism

Nothing to do with OO classes

Essentially the way to add an interface to a class after that class has already been defined

This means you can provide different "interface-style" functionality depending on your needs

For instance, you could provide your own response to "to String" functionality for a particular type

Property-based Testing

ScalaCheck in Scala, QuickCheck in Haskell

Specify properties about your code, let the tests provide the data

import org.scalacheck.Properties
import org.scalacheck.Prop.forAll

object ReverseProperties extends Properties("Test Cases") {
  property("reverse") = forAll { (s: String) =>
    s.reverse.reverse == s
  }
}
> test
[info] + Test Cases.reverse: OK, passed 100 tests.

Property-based Testing

object ReverseProperties extends Properties("Test Cases") {
  property("absolute") = forAll { (i: Int) =>
    Math.abs(i) >= 0
  }
}
> test
[info] ! Test Cases.absolute: Falsified after 8 passed tests.
[info] > ARG_0: -2147483648

Wrapping up

We've covered quite a bit of ground

Parametric polymorphism:

  • Adding constraints to our code appears to liberate authors from writing more than is necessary

Immutability:

  • By appearing to be less efficient with our data structures, we gain much more confidence in the code we write

Referential Transparency:

  • Functions cannot surprise us in any way

Wrapping up

Over to you

When trying to be purely functional in an impure language, you must be vigilant

Corner-cutting techniques are everywhere and can be very tempting

It's up to YOU to transmit a mentality of doing things the right way, and why you should do things that way

Thank you