A practical introduction to Functional Programming

Agenda

  • The Paradigms of Functional Programming
  • A brief history of Functional Programming
  • Functional Programming in practice
  • Discussion and Questions

The Goal

  • Give you a taste of Functional Programming
  • Motivate you to try Functional Programming at work or on your personal project

Who am I?

  • Yehonathan Sharvit @viebel
  • A mathematician
  • A coder
  • A pragmatic theorist
  • A freak of interactivity
  • Founded Audyx in 2013 - an Audiology Startup with 30K LOCs in Clojurescript
  • Author of Klipse - a simple client-side code evaluator pluggable on any web page
KLIPSE
  • A Web consultant: Full-Stack, clojure{,script}, ruby{, on rails}, javascript, react
  • A CodeMentor Xpert (You can hire me!)
me

The paradigms of Functional Programming

  • Functions are first class citizens
  • Declarative
  • Pure Functions
  • Immutability
  • Laziness

Functions - first class citizens

  • Functions can be passed to variable definitions
  • Functions can be passed as arguments to functions
  • Functions can return functions

Warning

Are you ready for a journey into the wonderland of Functional Programming?

journey

Functions - passed to variable definitions

Javascript

function foo() { return 42}
var bar = function() { return 43}
x = [foo(), bar()]

EcmaScript6

baz = () => 42
baz()

Clojure

(defn foox [] 42)
(def bax (fn [] 43))
[(foox) (bax)]

Ruby

def foow
  42
end
baw  = ->() { 43 }
[foow(), baw[]]

Functions - passed as arguments to functions

In function languages, you can create anonymous functions i.e. functions that don’t have a name.

Javascript

[1,2,3].map(function(x) { return x + 1})

EcmaScript6

[1,2,3].map((x) => x+1)

Clojure

(map inc [1 2 3])

Ruby

[1,2,3].map {|x|
  x + 1
  }

Functions that return functions

An important function in FP is partial (or curry).

partial takes a function and a list of arguments and returns a function where some arguments are fixed.

Let’s see it in action!

Javascript
We are going to use Ramda.

function add(x, y) { return x + y}
var add10 = R.partial(add, [10])
add10(19)

EcmaScript6

const add = (x, y) => x + y
const add10 = R.partial(add, [10])
add10(19)

Clojure

(defn add [x y] (+ x y))
(let [add10 (partial add 10)]
  (add10 32))

Ruby
In ruby, it’s called curry.

add  = ->(x,y) do x + y end
add10 = add.curry.(10)
add10.(9)

Exercise

Let’s write our own version of partial:

in ES6

partial = () => 1

If you prefer you can write it in clojure:

(defn my-partial [f x])

or in ruby:

def partial(f, x)
end
partial = (f, x) => (y) => f(x, y)
partial(add, 10)(9)

Declarative style

Let’s compare map and for in javascript.

With map, you don’t deal with low-level details
You only express what to do with each element of the array.

[1,2,3].map(x => x + 1)

With for, you have deal with low-level details.

You have to express:

  • how the array is iterated
  • what is the name of the index
  • how the elements are combined in the returned array
const arr = [1,2,3]
const f = x => x + 1
let retArr = []
for(let i = 0; i < arr.length; i++) {
  retArr.push(f(arr[i]))
}
retArr

Pure functions

A pure function

  • depends only on its parameters (not on a global state)
  • has no side effects

Pure functions are much simpler

  • to combine
  • to test
  • to debug
  • to understand
  • to refactor

Pure functions - no implicit dependencies

This function depends on a global parameter

const N = 1e9
const rand = () => Math.round(N*Math.random())
rand()

Let’s rewrite it as a pure function

const rand = (max) => Math.round(max*Math.random())
rand(1e9)

Pure functions - no side effects

This function writes the error to the console in case of a failure.

How will you test such a function?

const trySomething = (x) => {
  if (x) {
    return x*42
  }
  console.log(`I cannot do ${x}`)
}
trySomething()

Let’s rewrite it as a pure function.
We will return an array with:

  • the status
  • the data
trySomethingPure = (x) => {
  if (x) {
    return ["ok", x*42]
  }
  return ["error", `I cannot do ${x}`]
}
trySomethingPure(1)

Immutability - the problem

In non-functional languages, the default API mutates our objects.
You modify b and in fact, you modify also a!

In javascript:

const a = {size: 42};
b = a;
b.size = 43
a

In ruby:

a = {size: 42}
b = a
b['size'] = 33
a

In clojure, no problem: clojure data structures are immutable!

Immutability - the solution

Instead of mutating an object, you create a new version of it.
Usually, this is not effective.
But some languages (e.g. clojure) support immutability natively and provide an effective implementation of persistent data structure.

Clojure

(let [a {:size 42}
      b a
      c (assoc b :size 33)]
  [a b c])

In order to enjoy immutability in Javascript, you have to use a library like immutable.js

a = Immutable.fromJS({size: {shoes: 42 }})
b = a.setIn(["size", "shoes"], 43)
a

There is a immutable library available for ruby

Laziness

In functional languages, some sequences are lazy.
The elements are evaluated only when we really need them.

Let’s see some examples in clojure:

(def lazy-a (map (fn [x] (println "val:" x) x) [1 2 3]))

Nothing is printed, until we access the elements…​

(nth lazy-a 0)

Laziness - infinite sequences

Nothing prevents from lazy sequences to be infinite!

All the natural numbers

(def natural-numbers (range))
(take 10 natural-numbers)

Infinite repetition of an element

(def infinite-hello (repeat :hello))
(nth infinite-hello 100)

Don’t try to count the elements!

An infinite sequence of random numbers

(def infinite-random-numbers (repeatedly (partial rand-int 100)))
(nth infinite-random-numbers 345)

A brief history of Functional Programming

1930: λ-calculus

  • Alonzo Church discovers the λ-calculus
  • Everything is a (anonymous) function with one argument
  • No names in the language - only function argument
  • Even numbers are expressed as functions
  • 0 := λfx.x
  • 1 := λfx.f x
  • 2 := λfx.f (f x)

1958: LISP

  • John McCarthy invents LISP
  • It is the 1st FP language
  • Everything is a S-Expression: (+ 1 2 3) instead of 1 + 2 + 3 or +(1,2,3)
  • Only 7 terms: CAR, CDR, ATOM, LAMBDA, LABEL, COND, QUOTE

1995: Javascript the language of the browser

  • Brendan Eich is recruited by Netscape to do "scheme in the browser"
  • Eventually, he invents Javascript
  • Functions are 1st class citizens

2004: Scala - FP on the JVM

  • 2004 - Martin Odersky invents Scala
  • A JVM statically typed language with functional programming support
  • It is very complicated!!!

A brief history of Functional Programming (cont.)

  • 2007 - Rich Hickey invents Clojure - A practical dialect of LISP on top of JVM
  • 2011 - ClojureScript - Clojure rocks, Javascript Reaches!
  • 2013 - Facebook creates react.js - A functional javascript frontend framework
  • 2015 - Dan Abramov invents redux - A javascript library that imposes FP constraints on a frontend app
  • 2017, May 23 - A practical introduction to FP

Functional Programming in practice - Frontend

Many functional languages are transpiled in Javascript: Clojure, Scala, F#, Ocaml.

Functional Programming in practice - ClojureScript

The power of Clojure in the browser!

Functional Programming in practice - React.js

Insights

  • The UI is a (pure) function of the state (virtual DOM).
  • Functions compose very well.

Guidelines

  • Write functions that create UI components.
  • The state is a plain Javascript Object.
  • Do not mutate the state, create a new version of it (immutability).
  • Write functions that manipulate the state instead of the DOM (reduce the presence of the dirty stuff).
  • The framework will update the DOM efficiently.
  • Separate UI components from Logic components.

React.js in action - A pure component


ReactDOM.render(React.createElement(Square, {value: 42,
                                             onClick: () => alert("clicked")}), klipse_container)


<Square
value={26}
  onClick={() => alert("clicked")}
/>


Exercise: display the value when the square is clicked

React.js in action - modifying the state

class SquareLogic extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      val: 10
    }
  }
  increment() {
    let state = R.assoc('val', this.state.val + 1, this.state)
    this.setState(state)
  }
  render() {
    return (
      <Square value={this.state.val}
      onClick= {() => this.increment()}/>
      )
  }
}
window.SquareLogic = SquareLogic
<SquareLogic/>

Functional Programming in practice - Redux

Three principles (constraints):

  • Single source of truth
  • State is read-only
  • Changes are made with pure functions

Many Positive consequences:

  • Easy to test
  • Easy to track actions
  • Easy to visualize state of the app
  • Time travel (undo)
  • Send state over the wire
  • Store/Retrieve state from localStorage

Redux in action

Component

const mapDispatchToProps = (dispatch) => ({
  onClick() {
    dispatch(incrementSquareValue())
  }
})
const mapStateToProps = (state) => ({
  value: state.square.value
})
window.SquareRedux = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(Square)

Actions

incrementSquareValue = () => ({
  type: 'INCREMENT_SQUARE_VALUE'
})

Reducers

square = (state={value: 0}, action) => {
  switch(action.type) {
    case 'INCREMENT_SQUARE_VALUE':
      return R.assoc('value', state.value + 1, state)
    default:
      return state
                    }
}
app = Redux.combineReducers({square: square})

The store

store = Redux.createStore(app)

The App

<ReactRedux.Provider store={store}>
  <SquareRedux/>
</ReactRedux.Provider>


Playground

// store.dispatch(incrementSquareValue())
// store.getState()

Functional Programming in practice - Backend (Web server, micro services)

  • The development process of a web server is much more effective with languages that support FP
  • Examples: ruby, node.js, clojure, Scala.
  • Much less boilerplate than Object Oriented: no need to wrap everything in an object.
  • Dynamic type - Much simpler to manipulate data with maps (dictionaries) and arrays.
  • Sometimes, you need to leverage the java ecosystem.
  • No problem, let’s build a functional language that is compiled into Java ByteCode and creates a syntax for interop: interacting with the hosting language.
  • Examples: Scala, Clojure.

Functional Programming in practice - Big Data

When we need to write a (relatively small) worker that is part of a computation pipe, we need a language:

  • lightweight
  • easy to manipulate JSON-like data
  • combine functions
  • immutability is effective
  • stateless

Examples: Spark (scala), Storm (Clojure), Onyx (Clojure)

Functional Programming - more

  • Unit Tests
  • The REPL
  • Generative testing (a.k.a. Property based testing)
  • Macros
  • Homoiconicity
  • Pattern matching
  • Tail-call recursion

Going further

Discussion and Questions

questions

powered by Klipse /