Giving up the Object-Oriented Ghost

@morganlaco

github.com/mlaco

hello@morganlaco.com

Ghosts

Ghosts

  • Objects
  • Mutability
  • elsif
  • Loops

What is functional programming?

Functional vs Imperative

Lambda Calculus

Lambda Calculus

0 ≡ λf.λx.x
1 ≡ λf.λx.f x
2 ≡ λf.λx.f (f x)
3 ≡ λf.λx.f (f (f x))

 

Lambda Calculus

2 ≡ λf.λx.f (f x)

fn(f) {
  return fn(x) {
    return f(f(x))
}}

Lambda Calculus

2 ≡ λf.λx.f (f x)

fn(f) {
  return fn(x) {
    return f(f(x))
}}

Ghost 1: Objects

The Fours Awakens

class ConnectFour::Game
  def initialize
    @board = ConnectFour::Board.new
    # other instance variables
  end
  
  def play
    while continue_game
      get_move
      handle_move
    end
    
    display_result
  end
  # ...
end

ConnectFour::Game.new.play
defmodule ConnectFour do
  def start(_type, _args) do
    ConnectFour.initial_state
    |> do_turn
  end
  
  def do_turn(state) do
    ConnectFour.IO.display_board(state[:board])
    
    case get_new_state() do
      # ...
      new_state ->
        if ConnectFour.WinChecker.check(new_state[:board]) do
          ConnectFour.IO.do_win(new_state)
        else  
          do_turn(new_state)
        end
    end
  end

ConnectFour.start

Ghost 2: elsif

  def find_top(col, n\\0) do
    col_list = Tuple.to_list(col)
    [h|t] = col_list
    
    cond do
      !Enum.any?(t) && h != 0 ->
        {:error}
      h == 0 ->
        {:ok, n}
      true ->
        List.to_tuple(t) |> find_top(n+1)
    end
  end

0

1

-1

Key

#
  def find_top(col, n\\0) do
    col_list = Tuple.to_list(col)
    [h|t] = col_list
    
    cond do
      !Enum.any?(t) && h != 0 ->
        {:error}
      h == 0 ->
        {:ok, n}
      true ->
        List.to_tuple(t) |> find_top(n+1)
    end
  end
  def find_top(col, n\\0) do
    col_list = Tuple.to_list(col)
    [h|t] = col_list
    
    cond do
      !Enum.any?(t) && h != 0 ->
        {:error}
      h == 0 ->
        {:ok, n}
      true ->
        List.to_tuple(t) |> find_top(n+1)
    end
  end
h
  def find_top(col, n\\0) do
    col_list = Tuple.to_list(col)
    [h|t] = col_list
    
    cond do
      !Enum.any?(t) && h != 0 ->
        {:error}
      h == 0 ->
        {:ok, n}
      true ->
        List.to_tuple(t) |> find_top(n+1)
    end
  end
h
t
  def find_top(col, n\\0) do
    col_list = Tuple.to_list(col)
    [h|t] = col_list
    
    cond do
      !Enum.any?(t) && h != 0 ->
        {:error}
      h == 0 ->
        {:ok, n}
      true ->
        List.to_tuple(t) |> find_top(n+1)
    end
  end
h
t

Pattern Matching

Pattern Matching

[h|t] = col_list
  def check(remaining, []) do
    [next_row | other_rows] = remaining
    check(other_rows, next_row)
  end
  
  def check([], row) do
    check_row(row)
  end

  def check(remaining, row) do
    if check_row(row) do
      true
    else
      [next_row | other_rows] = remaining
      check(other_rows, next_row)
    end
  end
  def check(remaining, row) do
    if Enum.empty?(row) do
      [next_row | other_rows] = remaining
      check(other_rows, next_row)
    else
      if !Enum.empty?(remaining) do
        check_row(row)
      else
        if check_row(row) do
          true
        else
          [next_row | other_rows] = remaining
          check(other_rows, next_row)
        end
      end
    end
  end
  def find_top(col, n\\0) do
    col_list = Tuple.to_list(col)
    [h|t] = col_list
    
    cond do
      !Enum.any?(t) && h != 0 ->
        {:error}
      h == 0 ->
        {:ok, n}
      true ->
        List.to_tuple(t) |> find_top(n+1)
    end
  end

Ghost n: Mutability

What the fuzz?

immutability

before

after

foo
foo
foo = 4
foo = 5

4

5

immutability

before

after

foo
foo
foo = 4
foo = 5

4

5

4

foo

Why variables can be re-bound

Benefits of Immutability

FP in other languages

http://www.lispcast.com/imperative-mindset

Ghost n: Loops

Loops

(1..100).each do |i|
  do_something(i)
end

No Loops - why tho?

Recursion

  def fibonacci(n) do
    cond do
      n == 0
        0
      n == 1
        1
      true ->
        fibonacci(n, 0, 1)
    end
  end

  def fibonacci(n, a, b) do
    if n <= 0 do
      a
    else
      fibonacci(n - 1, b, a + b)
    end
  end

Recursion

def self.check(grid)
  grid.each_with_index do |column, column_index|
    n = 1
    last = 0
    column.each_with_index do |cell, row_index|
      if cell == last && cell != 0
        n += 1
      else
        n = 1
      end
      last = cell
      
      return true if n == 4
    end
  end
  def check_horizontal(board) do
    Transpose.transpose(board)
    |> check([])
  end

  def check(remaining, []) do
    [next_row | other_rows] = remaining
    check(other_rows, next_row)
  end
  
  def check([], row) do
    check_row(row)
  end

  def check(remaining, row) do
    if check_row(row) do
      true
    else
      [next_row | other_rows] = remaining
      check(other_rows, next_row)
    end
  end
  def check([next_row | other_rows], []) do
    check(other_rows, next_row)
  end
  
  # Equivalent to:
  def check(remaining, []) do
    [next_row | other_rows] = remaining
    check(other_rows, next_row)
  end

Tail Recursion

tail recursion

how does it work?

fibonacci(n)

fibonacci(n)

fibonacci(n)

fibonacci(n)

<process>

def fibonacci(n) do
  cond do
    n == 0
      0
    n == 1
      1
    true ->
      fibonacci(n, 0, 1)
  end
end

def fibonacci(n, a, b) do
  if n <= 0 do
    a
  else
    fibonacci(n - 1, b, a + b)
  end
end

tail recursion

how does it work?

fibonacci(n)

fibonacci(n)

fibonacci(n)

fibonacci(n)

<process>

def fibonacci(n) do
  cond do
    n == 0
      0
    n == 1
      1
    true ->
      fibonacci(n, 0, 1)
  end
end

def fibonacci(n, a, b) do
  if n <= 0 do
    a
  else
    fibonacci(n - 1, b, a + b)
  end
end

Loops

Enum.reduce( (1..100), \
  fn(x, acc) -> x + acc end )
  # => 5050

Enum.map( (1..5), \
  fn(x) -> 2 * x end )
  # => [2, 4, 6, 8, 10]

Enum.filter( (1..5), \
  fn(x) -> rem(x, 2) == 0 end )
  # => [2, 4]

What I told you was true...

Ghosts Review

Objects

Functional Purity

Mutability

Immutability

elsif

Pattern Matching

Loops

Recursion

resources

THANK YOU!!!

deck

By Morgan Laco

deck

  • 1,202