Functional Programming Makes you look smart
Functional programming? “If you ask 100 programmers for their definition, you’ll likely receive 100 different answers” - “The Joy of Clojure: Thinking the Clojure Way.”
FP - proposed definition Functional programming makes you look smart and others feel stupid
“Some may say Ruby is a bad rip-off of Lisp or Smalltalk, and I admit that” -- Matz
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 }
Time to code higher_order_functions.rb
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
Schonfinkeling 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.(:*)
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.(:*)
Time to code curry.rb
Immutability and side effect free function
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
Immutability (pure functions) ● 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
Time to code immutability.rb
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
Time to code memoization.rb
Recursion 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 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 }
Time to code recursion.rb
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.
Time to code enumerators.rb
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
Time to code lazy.rb
Log parser log_parser.rb
Homoiconicity
Homoiconicity Code is data, data is code
Thanks! @chenfisher code can be found here: https://github.com/chenfisher/functional-programming-with-ruby

Functional programming with Ruby - can make you look smart

Editor's Notes

  • #7 John McCarthy (lisp) vs Yukihiro Matsumoto (ruby)