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
- From Imperative to Functional and Back-Monads are for Functional Languages
- From Imperative to Functional: How to Make the Leap
- A Practical Introduction to Functional Programming
- StackOverflow: What are the core concepts in functional programming?
- Elixir Getting Started Guide
- Destroy All Ifs
- 16 Months of Functional Programming
- Learn You Haskell
- Is object immutability in functional programming inherently performance intensive?
- How to Switch from the Imperative Mindset
- Functional Programming Techniques With Ruby: Part I
- From Imperative to Functional Programming (for Absolute Beginners)
- Transposing a Matrix: Thinking Recursively in Elixir
- Don’t Be Scared Of Functional Programming
- Myth of the Day: Functional Programmers Don't Use Loops
THANK YOU!!!
deck
By Morgan Laco
deck
- 1,183