Functional Programming in an Object Oriented World – Functional Programming – How do we make it easier to understand



Functional Programming in an Object Oriented World – Functional Programming – How do we make it easier to understand

0 0


functional_programming_in_an_object_oriented_world

A talk extracted from a workshop by Aslam Khan

On Github bjoska / functional_programming_in_an_object_oriented_world

Functional Programming in an Object Oriented World

(a presentation created from a workshop by Aslam Khan)

Functional Programming

f(x)

First time

How do we make it easier to understand

And

how do we apply it to our objectified world?

Breaking it down...

Functions

fn(x) = x * 25 / 100
  • What would you say this function does?
  • Does one thing, same input always the same output.
function fn(x) {
  return x * 25 / 100;
}
(Javascript)

Three principles

  • Single responsibility
  • No side effects
  • Immmutability

Things we touch

  • Recursion
  • Tail Call Optimisation
  • Higher Order Functions

Things we can't touch

  • Monads
  • Monoids

1st Principle - Single Responsibility

def add_to_shopping_cart(item)
  self.items << item

  self.total += item.value
  self.total
end

(Ruby)

  • Single responsibility?
  • How do we fix it?
def add_to_shopping_cart(item)
  self.items << item
end

def total
  sum = 0

  for item in items do
    sum += item.value
  end

  sum
end

(Ruby)

What about logging?

def add_to_shopping_cart(item)
  self.items << item
  log("Item added to cart: #{item.sku}")
end

(Ruby)

Idealism vs Pragmatism

2nd Principle - No Side Effects

class Invoice < ActiveRecord::Base # ORM class
  belongs_to :customer

  def issue
    customer.add_amount(self.amount)
    self.status = 'Issued'
    customer.total
  end
end

(Ruby)

  • Side effects?
  • How do we fix it?
class Customer < ActiveRecord::Base
  has_many :invoices
  def total_amount
    invoices.where(status: 'Issued').reduce(0) do |total, invoice|
      total += invoice.amount
      total
    end
  end
end

class Invoice < ActiveRecord::Base
  belongs_to :customer
  def issue
    self.status = 'Issued'
  end
end

(Ruby)

So we fixed the side effects, but at what cost?

  • Responsibility travels up the call stack
  • Atomic Consistency vs Consistency with Latency
  • Caused by the fact that we have been trained by our relational data store

"We can handle latency as we do in life." - Aslam Khan

3rd Principle - Immutability

def add_to_shopping_cart(item)
  self.items << item
  self.items
end

(Ruby)

  • Immutable?
  • How would we achieve immutability?

Pragmatic Solution

def add_item_to_shopping_cart(shopping_cart, item)
  shopping_cart.add item
  shopping_cart
end

(Ruby)

Pure Immutability

def add_item_to_shopping_cart(shopping_cart, item)
  # Dirty hack to deep copy an object in Ruby
  shopping_cart = Marshal.load(Marshal.dump(shopping_cart))
  shopping_cart.add item
  shopping_cart
end

(Ruby)

next_value() {
  this.n += 1;
  return this.n;
}

(Javascript)

next_value(current_int) {
  return current_int + 1;
}

(Javascript)

Recursion

Common issue

Blowing the stack

Solution

Tail Call Optimisation

Recursion

function sum(numbers) {
  if(numbers.length == 0) {
    return 0;
  }
  return numbers[0] + sum(numbers.slice(1));
}

(Javascript)

Tail Call

function sum(acc, numbers) {
  if(numbers.length == 0) {
    return acc;
  }
  return sum(acc + numbers[0], numbers.slice(1));
}

(Javascript)

Higher Order Functions

Functions that take functions

Regular Function

function sum(acc, numbers) {
  if(numbers.length == 0) {
    return acc;
  }
  return sum(acc + numbers[0], numbers.slice(1));
}

(Javascript)

Higher Order Function

function reduce(acc, collection, fn) {
  if(collection.length == 0) {
    return acc;
  }
  return reduce(fn(acc, collection[0]), collection.slice(1), fn);
}
function sum(numbers) {
  return reduce(0, numbers, function(a, b) {
    return a + b;
  });
}
function count(numbers) {
  return reduce(0, numbers, function(a, _) {
    return a + 1;
  });
}

(Javascript)

Easy Rigth?

Questions?