functional programming with ruby - can make you look smart
DESCRIPTION
Functional programming can make you look smart and others feel stupid. Learn how with Ruby code can be found here: https://github.com/chenfisher/functional-programming-with-ruby Youtube: https://www.youtube.com/watch?v=Qzoh8w4OPtUTRANSCRIPT
Functional ProgrammingMakes you look smart
“If you ask 100 programmers for their definition, you’ll likely receive 100 different answers”
- “The Joy of Clojure: Thinking the Clojure Way.”
Functional programming?
FP - proposed definition
Functional programming makes you look smart and others feel stupid
-- Matz
“Some may say Ruby is a bad rip-off of Lisp or Smalltalk, and I admit that”
Higher order functions
Higher order functions
Do one or more of the following:● Accept a function as an argument● Return a function as the return value
Higher order functions
Ruby introduces blocks, procs and lambdas as an instrument for higher order functions
[2, 3, 1].sort { |x,y| y <=> x }
higher_order_functions.rb
Time to code
Higher order functions
● map, select, inject, reduce, etc.
Higher order functions
Prefer this:
result = ["abraham", "isaac", "jacob"].map do |name|
name.capitalizeend
Higher order functions
Over this:
result = []
["abraham", "isaac", "jacob"].each do |name|
result << name.capitalizeend
Schonfinkeling
Schonfinkeling
Is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument -- Wikipedia
f(x, y, z) = f’(x)(y)(z)
Moses Schonfinkel
Proc#schonfinkel (ruby 1.9)
calc = proc { |op, a, b| a.send(op,
b) }
Schonfinkeling
Proc#schonfinkel (ruby 1.9)
calc = proc { |op, a, b| a.send(op,
b) }
add = calc.schonfinkel.(:+)
sub = calc.schonfinkel.(:-)
mult = calc.schonfinkel.(:*)
Schonfinkeling
Haskell Brooks Curry
Currying
Is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument -- Wikipedia
f(x, y, z) = f’(x)(y)(z)
Currying
Proc#curry (ruby 1.9)
calc = proc { |op, a, b| a.send(op,
b) }
add = calc.curry.(:+)
sub = calc.curry.(:-)
mult = calc.curry.(:*)
curry.rb
Time to code
and side effect free function
Immutability
Immutability
Imperative programming:A programming paradigm that describes computation in terms of statements that change a program state
Immutability
● Data is immutable (cannot be changed)● Everything is stateless
● Output depends only on input (no state)● Will always have the same output for the
same input● No side effect● Memoization● Idempotence
Immutability (pure functions)
● Parallelize● Concurrency● result = f(a) + f(b) # can be parallelized● Testing is easier
Immutability (pure functions)
immutability.rb
Time to code
Immutability
Prefer this:new_options = options.merge({key: value})
Over this:options.merge!({key: value})
Always prefer not to use the bang (!) version of a function
Memoization
Memoization
Pure functions always return the same output for the same input
No need to re-compute on every call
Memoization
# simple memo we use all the time (||=)
def fact(fact_id)
@fact[fact_id] ||= fetch_fact_from_db(fact_id)end
memoization.rb
Time to code
Tail call optimization
Recursion
Immutable data! how do we loop ?!We cannot do this:
i = 0 while i < 10
i = i + 1end(and we shouldn’t - this is ugly)
Recursion
Recursion
def factorial(n)
n < 2 ? 1 : n * factorial(n-1)end
factorial 10000 # stack level too deep
Recursion
def tail_factorial(n, r)
n < 2 ? r : tail_factorial(n-1, n*r)end
tail_factorial 10000 # OK (if tco enabled in ruby)
Recursion
# enable tail call
optimizationRubyVM::InstructionSequence.compile_option = {
:tailcall_optimization => true,
:trace_instruction => false
}
recursion.rb
Time to code
Enumerators
Enumerators
What is the sum of the first 10 numbers divided by 3?
Enumerators
# imperative - focuses on 'how'
count = 0; sum = 0; i = 0while count < 10
if i % 3 == 0
sum += i
count += 1
end
i += 1end
puts sum# functional (using enumerators) - focuses on 'what'
(0..10000).select { |x| x%3 == 0 }.take(10).reduce(:+)
Enumerators
select, each, inject, etc.. All use enumerators:[1, 2, 3].select.class => # enum
Enumerators
If you implement Enumerable in your class, you will have the following methods out of the box:● select● inject● take● etc.
enumerators.rb
Time to code
Laziness
Laziness
What is the sum of the first N numbers divided by 3?(0..Float::INFINITY).select { |x| x%3 == 0 }.take(N).reduce(:+)
Note: Only Chuck Norris can run this
Laziness
What is the sum of the first N numbers divided by 3?(0..Float::INFINITY).lazy.select { |x| x%3 == 0 }.take(N).reduce(:+)
Enumerator#Lazy introduced in Ruby 2.0
lazy.rb
Time to code
log_parser.rb
Log parser
Homoiconicity
Code is data, data is code
Homoiconicity
@chenfisher
Thanks!
code can be found here: https://github.com/chenfisher/functional-programming-with-ruby