Functional Programming in RubyKoen HandekynCEO
Learning FPit’s (sometimes) (a bit) difficult (in the beginning)        - you need to retrain your brain -                 ...
History‣ Lambda calculus: 1930’s (Alonzo Church)‣ Lisp: 1950’s, multi-paradigm language strongly inspired by lambda-calcul...
Isn’t all programming functional?‣ FP proceeds from a startling premise—that we construct programs using only pure functio...
Advantages‣ No mutable data and hence:‣ No side effects, no implicit hidden state, less bugs.‣ No variables ! (only values...
FP vs OO‣ Whereas an object-oriented mindset will foster the approach of  defining an application domain as a set of nouns ...
Immutable Objects‣ An OO pattern that actually originates in FP world‣ ISO changing a data structure, don’t modify in plac...
Ruby and FP‣ Ruby is an imperative and OO language with closure support‣ But we can apply (some) FP principles‣ It allows ...
Recursion# fibonacci functional through recursiondef fib(count, a = 1, b = 1 , r = [])             r is the               ...
or also - As opposed to ?‣ Another look at it is to compare imperative programming languages with declarative programming ...
Taking a look at <<Enumerable >>
First introduce Closure‣ Closure = an anonymous function block together with a referencing  environment‣ Where? javascript...
Enumerable#select(1..10).select { |x| x.odd? } => [1, 3, 5, 7, 9]# imperative styleodds = [](1..10).each do |n|  odds << n...
Enumerable#partition(1..10).partition { |x| x.odd? } => [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]# imperative stylep = [[],[]](1...
Enumerable#map(1..10).map { |x| x * 2 } => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]# imperative styledoubles = [](1..10).each ...
inject & reduce & foldl & foldr
foldl & foldr
Enumerable#reduce(1..10).reduce { |x,y| x + y } # repeat sum=> 55# imperative stylesum = 0(1..10).each do |n|  sum += nend...
Enumerable#reduce# repeat sum, start with 0(1..10).reduce(0) { |x,y| x + y }=> 55# repeat multiply, start with 1(1..10).re...
Enumerator#group_by(1..6).group_by { |i| i%3 }=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}# imperative stylep = {}(1..6).each do |...
Enumerable#sort%w(rhea kea flea).sort#=> ["flea", "kea", "rhea"](1..10).sort {|a,b| b <=> a}#=> [10, 9, 8, 7, 6, 5, 4, 3, ...
(: take a breath :)
Currying‣ In mathematics and computer science, currying is the technique of transforming a function that takes multiple ar...
Functions as Values in Rubyinc = lambda { |x| x + 1 }inc = ->(x) { x + 1 }inc.(4) => 5add = lambda { |x,y| x + y }add = ->...
Constants are Functionsconstant = ->(c,x) { c }.curryhello = constant.(“hello”)hello.(1) => “hello”hello.(“eugen”) => “hel...
Compositionidentity = ->(x) { x } # a closurepower = ->(base, x) { base**x }.currysum_of = ->(f, xs, i=0) { xs.inject(i) {...
Constants are Functionsconstant = ->(c,x) { c }.currysum_of = ->(f, r, i=0) { r.inject(i) { |a,i| a+=f.(i) } }.curryword_c...
Currying and Partial Evaluationpower = ->(base, x) { base**x }.currypower.(10,2) # => 100power.(10).(2) # => 100power2 = p...
Currying and Partial Evaluation# meta functional programming ;)send = ->(m, o) { o.send(m) }.currylength_of = send.(:lengt...
More Composition# from previouslength_of = ->(o) { o.length }sum_of = ->(f, r, i=0) { r.inject(i) { |a,i| a+=f.(i) } }.cur...
Playing with boxesbox1 = { width: 230, heigth: 304 }box2 = { width: 340, heigth: 243 }by_key = ->(k, o) { o[k] }.curryby_w...
More boxescompose = ->(f,g,x) { g.(f.(x)) }.currysquare = ->(x) { x*x }square_width = compose.(by_width).(square)square_wi...
More compositionmap = ->(f, a) { a.map { |x| f.(x) }}.curry # turn method into lambdasquares = ->(a) { map.(square).(a) }s...
More compositionbook = [ %w(this is a long sentence), %w(this is a short), %w(yes) ]foldl = ->(f, arr) { arr.inject { |r, ...
More Compositionbook = [ %w(this is a long sentence), %w(this is a short), %w(yes) ]flatten = ->(arr) { arr.flatten } # co...
Liquer Storesliquer_stores   = []liquer_stores   << {   name:   "total", d: 2.0, price: 32.0 }liquer_stores   << {   name:...
Imperative Liquerdef cheap_boose_nearby (liquer_stores)  min = liquer_stores[0][:price]  liquer_stores.each do |store|    ...
Declarative Liquerdef expensive_boose_nearby (liquer_stores)  nearby = ->(d, x) { x[:d] < d }.curry  near = nearby.(5.0)  ...
Generators / Sequence / Infinite ...# Functions that return a sequence of values# Here: fibonacci as infinite yielderfibona...
Enumerable as Classclass Fibs  include Enumerable  def each    a = b = 1;    loop { yield a; a,b = b,a+b };  endendFibs.ne...
Merci
Upcoming SlideShare
Loading in …5
×

Functional programming in ruby

1,042 views

Published on

What the title says. Presented at Timisoara Ruby Community Event. Hosten by UnifiedPost Romania.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,042
On SlideShare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
14
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Functional programming in ruby

  1. 1. Functional Programming in RubyKoen HandekynCEO
  2. 2. Learning FPit’s (sometimes) (a bit) difficult (in the beginning) - you need to retrain your brain - but rewarding it’s fun & productive
  3. 3. History‣ Lambda calculus: 1930’s (Alonzo Church)‣ Lisp: 1950’s, multi-paradigm language strongly inspired by lambda-calculus (symbolic manipulation, rewriting)‣ ML-family: 1970’s, general purpose functional programming language with type inference‣ Haskell: 1987, 1998, 2010, open standard for functional programming research‣ Other: Clean, F#, Scheme, Scala, Clojure, XSLT, Erlang, SQL, ...
  4. 4. Isn’t all programming functional?‣ FP proceeds from a startling premise—that we construct programs using only pure functions, or functions that avoid side effects like changing variables(state), writing to a database or reading from a file.‣ In a pure functional programming language, everything is a function. we can pass them around and “calculate” with them (combine, evaluate or partially evaluate, etc).‣ A function defines a mapping from a “Domain” into the “Codomain” (image, range)
  5. 5. Advantages‣ No mutable data and hence:‣ No side effects, no implicit hidden state, less bugs.‣ No variables ! (only values) => optimizations‣ Can (more easily) be parallelized over cpu’s (multicore), etc. => no locks, no race conditions, no deadlocks‣ Enables Provability (both for humans and computers)
  6. 6. FP vs OO‣ Whereas an object-oriented mindset will foster the approach of defining an application domain as a set of nouns (classes) [ person, ticket, etc ]‣ The functional mind will see the solution as the composition or verbs (functions) [ register, sell ]‣ Though both programmers may in all likelihood generate equivalent results, the functional solution will be more succinct, understandable, and reusable. Grand claims indeed!
  7. 7. Immutable Objects‣ An OO pattern that actually originates in FP world‣ ISO changing a data structure, don’t modify in place but create a new object.‣ In Ruby this is typically the default. Methods that don’t follow this principle are assumed ‘dangerous’ and are typically marked with a ‘!’ • name.reverse => returns a new string that contains the reversed name • name.reverse! => replaces the name with the reversed value
  8. 8. Ruby and FP‣ Ruby is an imperative and OO language with closure support‣ But we can apply (some) FP principles‣ It allows to mix and match OO with FP programming style A bit of pattern matching‣ You can’t assume immutability ... it’s a choice • x, *xs = [1,2,3,4]‣ No (real) pattern matching (yet) x => 1 xs => [2,3,4]‣ No lazy evaluation (yet) • a,b,tail = [1,2,3,4] a => 1 b => 2 tail => [3,4]
  9. 9. Recursion# fibonacci functional through recursiondef fib(count, a = 1, b = 1 , r = []) r is the accumulator count == 0 ? r : fib(count-1, b, a+b, r << a)endfib(10) # => [1,1,2,3,5,... needs a bit of practice but once you get it ...
  10. 10. or also - As opposed to ?‣ Another look at it is to compare imperative programming languages with declarative programming languages‣ Imperative = emphasize on how something is computed‣ Declarative = emphasize on what is to be computed and not on how‣ Imperative is counterintuitive when you’re used to imperative programming
  11. 11. Taking a look at <<Enumerable >>
  12. 12. First introduce Closure‣ Closure = an anonymous function block together with a referencing environment‣ Where? javascript, python, ruby, PHP, C# 2.0, java 8! :)
  13. 13. Enumerable#select(1..10).select { |x| x.odd? } => [1, 3, 5, 7, 9]# imperative styleodds = [](1..10).each do |n| odds << n if n.odd?end
  14. 14. Enumerable#partition(1..10).partition { |x| x.odd? } => [[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]]# imperative stylep = [[],[]](1..10).each do |n| p[0] << n if n.odd? p[1] << n unless n.odd?end
  15. 15. Enumerable#map(1..10).map { |x| x * 2 } => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]# imperative styledoubles = [](1..10).each do |n| doubles << n*2end
  16. 16. inject & reduce & foldl & foldr
  17. 17. foldl & foldr
  18. 18. Enumerable#reduce(1..10).reduce { |x,y| x + y } # repeat sum=> 55# imperative stylesum = 0(1..10).each do |n| sum += nendsum # => 55
  19. 19. Enumerable#reduce# repeat sum, start with 0(1..10).reduce(0) { |x,y| x + y }=> 55# repeat multiply, start with 1(1..10).reduce(1) { |x,y| x * y }=> 3628800# or ‘for real’ :)(1..10).inject(:+) => 55(1..10).inject(:*) => 3628800
  20. 20. Enumerator#group_by(1..6).group_by { |i| i%3 }=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}# imperative stylep = {}(1..6).each do |n| k = n%3 p[k] ||= [] p[k] << nend
  21. 21. Enumerable#sort%w(rhea kea flea).sort#=> ["flea", "kea", "rhea"](1..10).sort {|a,b| b <=> a}#=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]# imperative style# ... have fun ...
  22. 22. (: take a breath :)
  23. 23. Currying‣ In mathematics and computer science, currying is the technique of transforming a function that takes multiple arguments (or a tuple of arguments) in such a way that it can be called as a chain of functions, each with a single argument (partial application). It was originated by Moses Schönfinkel and later re-discovered by Haskell Curry.‣ curry(int, int => bool) = int => (int => bool)
  24. 24. Functions as Values in Rubyinc = lambda { |x| x + 1 }inc = ->(x) { x + 1 }inc.(4) => 5add = lambda { |x,y| x + y }add = ->(x,y) { x + y }add.(2,3) => 5
  25. 25. Constants are Functionsconstant = ->(c,x) { c }.curryhello = constant.(“hello”)hello.(1) => “hello”hello.(“eugen”) => “hello”
  26. 26. Compositionidentity = ->(x) { x } # a closurepower = ->(base, x) { base**x }.currysum_of = ->(f, xs, i=0) { xs.inject(i) { |a,i| a+=f.(i) } }.currysum_of_numbers = sum_of.(identity) a Higher Ordersum_of_power2s = sum_of.(power.(2)) function takes asum_of_squares = sum_of.(square) function as parameter# usagesum_of_numbers.(0..10) # => 55sum_of_squares.(0..10) # => 2047sum_of.( power.(3) ).(0..10) # => 88573
  27. 27. Constants are Functionsconstant = ->(c,x) { c }.currysum_of = ->(f, r, i=0) { r.inject(i) { |a,i| a+=f.(i) } }.curryword_count = sum_of.( constant.(1) )word_count.( %w(welcome to the world of fp) ) => 6
  28. 28. Currying and Partial Evaluationpower = ->(base, x) { base**x }.currypower.(10,2) # => 100power.(10).(2) # => 100power2 = power.(2) # partial evaluationpower2.(3) # => 8add = ->(x,y) { x + y }.curryadd.(2,3) => 5inc = add.(1) # partial evaluationinc.(3) => 4
  29. 29. Currying and Partial Evaluation# meta functional programming ;)send = ->(m, o) { o.send(m) }.currylength_of = send.(:length)length_of.(“koen”) # => 4length_of.([12,4,25,32,[2,2]]) # => 5
  30. 30. More Composition# from previouslength_of = ->(o) { o.length }sum_of = ->(f, r, i=0) { r.inject(i) { |a,i| a+=f.(i) } }.curry# composetotal_length_off = sum_of.(length_of)# usetotal_length_off.( [koen,ciprian,eugen] ) # => 16total_length_off.( [ [2,3,4], "koen", [3,4,5] ] ) # => 10
  31. 31. Playing with boxesbox1 = { width: 230, heigth: 304 }box2 = { width: 340, heigth: 243 }by_key = ->(k, o) { o[k] }.curryby_width = by_key.(:width)taller = ->(f, a, b) { f.(a) > f.(b) }.currytaller.(by_width, box1, box2) # => falsetaller.(by_key.(:heigth)).(box1,box2) # => true
  32. 32. More boxescompose = ->(f,g,x) { g.(f.(x)) }.currysquare = ->(x) { x*x }square_width = compose.(by_width).(square)square_width.(box1) # => 52900square_height = compose.(by_key.(:heigth)).(square)square_height.(box1) # => 92416
  33. 33. More compositionmap = ->(f, a) { a.map { |x| f.(x) }}.curry # turn method into lambdasquares = ->(a) { map.(square).(a) }squares = map.(square) # nicer through composition, not ? :)sum = ->(a) { a.inject(0,:+) }square_of_sum = compose.(sum).(square)after = composesum_of_squares = after.(squares).(sum)square_of_sum.([2,3]) # => 25sum_of_squares.([2,3]) # => 13square_of_sum.([2,3,4]) # => 81sum_of_squares.([2,3,4]) # => 29
  34. 34. More compositionbook = [ %w(this is a long sentence), %w(this is a short), %w(yes) ]foldl = ->(f, arr) { arr.inject { |r, x| f.(r, x) } }.curryadd = ->(a,b) { a+b }div = ->(a,b) { a*1.0/b }length = ->(x) { x.length }sum = foldl.(add)divide = foldl.(div)pair = parallel = ->(f,g,x) { [f.(x), g.(x) ] }.curryaverage = ->(a) { sum.(a) / length.(a) }average = after.( pair.(sum).(length) ).(divide)average_wordcount = after.( map.(length) ).(average) # => 3.33
  35. 35. More Compositionbook = [ %w(this is a long sentence), %w(this is a short), %w(yes) ]flatten = ->(arr) { arr.flatten } # convert to lambdawordlengths = after.( flatten ).( map.(length) )average_wordlength = after.(wordlengths).(average)average_wordlength.(book) # => 3.4
  36. 36. Liquer Storesliquer_stores = []liquer_stores << { name: "total", d: 2.0, price: 32.0 }liquer_stores << { name: "shell", d: 2.6, price: 28.5 }liquer_stores << { name: "esso", d: 3.2, price: 41.0 }liquer_stores << { name: "q8", d: 3.5, price: 22.0 }liquer_stores << { name: "shell", d: 4.5, price: 19.0 }liquer_stores << { name: "q8", d: 5.5, price: 18.0 }
  37. 37. Imperative Liquerdef cheap_boose_nearby (liquer_stores) min = liquer_stores[0][:price] liquer_stores.each do |store| if store[:d] < 5.0 then price = store[:price] price = price * 0.9 if store[:name] == "shell" min = price if price < min end end minend
  38. 38. Declarative Liquerdef expensive_boose_nearby (liquer_stores) nearby = ->(d, x) { x[:d] < d }.curry near = nearby.(5.0) myPrice = ->(x) { x[:name] == "shell" ? x[:price]*0.9 : x[:price] } liquer_stores. find_all(&near). recognize collect(&myPrice). SQL ? maxend
  39. 39. Generators / Sequence / Infinite ...# Functions that return a sequence of values# Here: fibonacci as infinite yielderfibonacci = Enumerator.new do |list| a = b = 1 loop { list.yield a; a,b = b,a+b }endfibonacci.take(10) # [1, .. , 55]fibonacci.take(15) # [1, .. , 377, 610]
  40. 40. Enumerable as Classclass Fibs include Enumerable def each a = b = 1; loop { yield a; a,b = b,a+b }; endendFibs.new.take(10)
  41. 41. Merci

×