Your SlideShare is downloading. ×
Functional programming in ruby
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Functional programming in ruby

435
views

Published on

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

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

Published in: Technology

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
435
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
9
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Functional Programming in RubyKoen HandekynCEO
  • 2. Learning FPit’s (sometimes) (a bit) difficult (in the beginning) - you need to retrain your brain - but rewarding it’s fun & productive
  • 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. 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. 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. 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. 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. 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. 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. 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. Taking a look at <<Enumerable >>
  • 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. 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. 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. 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. inject & reduce & foldl & foldr
  • 17. foldl & foldr
  • 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. 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. 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. 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. (: take a breath :)
  • 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. 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. Constants are Functionsconstant = ->(c,x) { c }.curryhello = constant.(“hello”)hello.(1) => “hello”hello.(“eugen”) => “hello”
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Enumerable as Classclass Fibs include Enumerable def each a = b = 1; loop { yield a; a,b = b,a+b }; endendFibs.new.take(10)
  • 41. Merci