DA FUNC

@gypsydave5     @deniseyu21

A quick-and-dirty intro to FP

Stuff we're gonna talk about

  1. Programming Paradigms
  2. FP Language Features
  3. Show me how this damn thing works!
  4. Dave explains why you should care
  5. Questions?

Part I

Programmin' 

paradigms

Object
Oriented

Functional

Imperative

Declarative

Imperative

Telling the computer how to do something

dogs_called_erik = [];

for (i = 0, i < dogs.length, i += 1) do
  if dogs[i].name == 'Erik'
    dogs_called_erik.push dogs[i] 
  end
end

Declarative

Tell the computer what to do, but not how to do it

SELECT * FROM dogs
WHERE dogs.name = 'Erik'

In modern programming languages,
imperative and declarative are relative terms

Object Orientation

"I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages... I wanted to get rid of data."

 

Alan Kay, creator of Smalltalk

OO uses state to store and manipulate data.

Methods are ways for objects to communicate.

class Dog
  def initialize(name)
    @name = name
  end

  def name
    @name
  end

  def rename(new_name)
    @name = new_name
  end

  def woof
    "Woof!"
  end
end

Functional

Key differences from OO

  • No objects
  • No messages
  • Lots of functions
  • Much data
  • Wow

Functional programming only needs two things:

data and functions.

What do we mean by data?

"data"

1432043295

["dave" "denise" "josh"]

{
    "languages": {
        "object-oriented": [
            "ruby",
            "smalltalk"
        ],
        "functional": [
            "clojure"
        ]
    }
}

What's a function?

f(x) = x + 1

y = f(x), where f(x) = x + 1

function woof (name) {

  return "Woof " + name + "!";

}

Functions map
inputs to outputs.

(Similar to methods, 
but without homes) 

Language design is rarely ever completely OO or FP.

(Except maybe Haskell.)

Part II

Functional language features

1. First class functions

Not just a really, really fancy function.

First-class functions can be:

  • passed to other functions

  • returned by other functions

In languages without first-class functions, it's hard to get a reference to a function without calling it.

def say_hello
    "hello!"
end

say_hello

# "hello!"

# no, stupid Ruby! tell me what 'say_hello' is

OO languages like Ruby and Java 
get around this with lambdas

say_hello = lambda { "hello!" }

say_hello

# <Proc:0x007ff9b2a59138@(irb):1 (lambda)>

say_hello.call

# "hello!"

You would be correct in thinking that 
this feels clumsy and obfuscating.

In languages with first-class functions,
the default behaviour is to REFERENCE, not call functions.

function yo () {
    return "yo!";
}

yo

// [Function: yo]

yo()

// "yo!"

This lets us
reason about functions
in the same way that we already
reason about objects
in OO paradigms.

2. Referential transparency

Functions can always be replaced
with their results
without changing
overall behaviour.

Because 

// so if

function sum (a, b) { 
    return a + b; 
}

sum(1, sum(2, 3))

// is the same as

sum(1, 5)

Given an input, no matter how many times the function is called, the output will be the same.

How do
non-referentially-transparent functions look?

Let's see some contrived examples...

function sum (a, b) {
    const n = Math.random();

    if (n < 0.5) {
        return a + b;
    } else {
        return 'wtf';
    }
}

sum(3, sum(1, 2))

// Maths only works sometimes here
let counter = 0;

function next (number) {
    counter++;

    return number + counter;
}

next(1); // 2
next(1); // 3
next(1); // 4

Referential transparency is 
determinism inside the 
scope of the function.

What about everything that happens
outside the function?

 

Glad you asked...

3. Functional purity

Not just a really, really innocent function. 

Pure functions cause no side effects. 

No mutation of state

No unexpected outputs

Return what you mean,
and nothing more!

let mutableVar = '';

function emitDirtyState () {
    mutableVar += 'blah ';
    return mutableVar;
}

This is impure:

function emitDirtyState () {
    console.log("I'm a side effect");
    return "yo";
}

This is impure:

function emitDirtyState () {
    someNewGlobalVar = "wahoo!";
    return "wtf?!?";
}

// you laugh, but this 
// has been done before

This is REALLY impure:

4. Higher-order functions

Functions are data that can be
inputs to other functions.

You've already done this if you've written a block in Ruby

cats = ["Felix", "Tom", "Puss in Boots", "Garfield"]

cats.map { |cat| cat.upcase }

// now JS!

cats.map(function(cat) { return cat.toUpperCase() })

Higher-order functions let us abstract 
new functions out of old functions

It's hard to explain this without examples, so...

Part III

Show me how this damn thing works!

Without fp principles, modifying behaviour of existing functions
can be really tedious

// Suppose you have this amazing code

function addTwoTogether (a, b) {
  return a + b;
}

Product owner decides that the user will only ever be adding 3. always.

// new code for new requirements

function addThree (a) {
  return a + 3;
}
// old code

function addTwoTogether (a, b) {
  return a + b;
}

Product owner decides, actually, user will need to add 5 instead of 3. but definitely only 5, and always 5!

// new code for new requirements

function addFive (a) {
  return a + 5;
}
// old code

function addThree (a) {
  return a + 3;
}

Actually, PO doesn't know what the end user really wants. let's just build something flexible for changing business requirements...

A brief recap:

function addTwoTogether (a, b) {
  return a + b;
}

// Great, but we only ever want to add 3

function addThree (a) {
  return a + 3;
}

// JK, requirements change, now let's add 5, definitely

function addFive (a) {
  return a + 5;
}

// User didn't like it, add 10 now... and so on and so on...
// and maybe more requirements change later
// shit, if only we had a way to automate this like the
// lazy-ass programmers we are.....

Behold... currying

no, not that kind

Haskell Curry 

(just in case you didn't know)

// A simple arity-of-2 curry function

function curryTwoInputs (fun) {
  return function (a) {
    return function (b) {
      return fun (a, b);
    }
  }
}

A few libraries like Lodash and Rambda have helper functions do to this

var _ = require('lodash');

function addTwoTogether (a, b) {
  return a + b;
}

var curriedAddTwoTogether = _.curry(addTwoTogether);

var addTen = curriedAddTwoTogether(10);

addTen(5); // 15

// We can define new ones without manually writing a new function

var addEighteen = curriedAddTwoTogether(18);

addEighteen(20); // 38

Besides currying, another really cool functional thing you can do...

Functional composition

Remember maths class?

f(x) = 2x

g(x) = x + 3

h(x) = f(g(x))

h(1) = f(g(1)) = f(4) = 8

Lisp-based languages
read similarly to maths

(And don't use the word "function" as much)

Most of the time,
composed Functions get evaluated
right to left
(inner to outer)

(defn add 
  [a b] 
  (+ a b))

(add 4 5) ;; 9

(add 5 (add 3 (add 1 2))) ;; 11
// a simple arity-of-2 compose

function simpleCompose(fun1, fun2) {
  return function (x) {
    return fun1(fun2(x))
  }
}

once again, lots of great functional js helper libraries here

var _ = require('ramda');

addTwo(2) // => 4 ... remember this guy?
addTwo(addTen(2)) // => 14 ... we can keep feeding them in...

// We can toss functions together arbitrarily now

var addTwoThenTen = _.compose(addTwo, addTen);

addTwoThenTen(1); // 13
addTwoThenTen(100); // 114
var addFiveThenSeven = simpleCompose(addFive, addSeven);

addFiveThenSeven(10); // 22
addFiveThenSeven(0); // 12

var addAllTheThings = simpleCompose(addTwoThenTen, addFiveThenSeven);

It turns out, functional composition is a lot like stacking things together 

Part IV

4. Dave rants for a while

def log_formatter(log)
  log.group_by do |entry| 
    entry[:time]
  end.values.map do |entries_by_time| 
    entries_by_time.reduce(:merge) 
  end
end

QUESTIONS?????

Dysfunctional Programming

By Denise Yu

Dysfunctional Programming

  • 608
Loading comments...

More from Denise Yu