Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

A Gentle Introduction to Functional Paradigms in Ruby

376 views

Published on

Lots of patterns are encountered in large ruby codebases which can be expressed more elegantly in a functional manner. Ruby provides a number of built-in facilities to enable functional style programming.

This talk aims to provide a jumping-off point for rubyists who are used to imperative style programming and mutable state a jumping-off point for exploring functional paradigms.

Published in: Technology, Business
  • Be the first to comment

A Gentle Introduction to Functional Paradigms in Ruby

  1. 1. FUNCTIONALRUBY
  2. 2. FIRST-CLASSFUNCTIONS You already use these # Print each number from 1-30 in hexadecimal (1..30).each do |number| puts number.to_s(16) end
  3. 3. FIRST-CLASSFUNCTIONS Changing the example slightly: # Create a lambda which prints a number in hexadecimal puts_in_hex = lambda do |number| puts number.to_s(16) end (1..10).each { |i| puts_in_hex.call(i) }
  4. 4. FIRST-CLASSFUNCTIONS This can be further simplified: puts_in_hex = lambda do |number| puts number.to_s(16) end (1..10).each(&puts_in_hex)
  5. 5. ASIDE:THEAMPERSAND The ampersand indicates that we're working with a block def use_block(&block) block.call(2) end use_block do |number| number * 2 end # => 4
  6. 6. ASIDE:THEAMPERSAND It also causes a viariable to be interpreted as a block. def use_block(&block) block.call(2) end multiply_two = lambda do |number| number * 2 end use_block(&multiply_two) # => 4
  7. 7. ASIDE:SYMBOL#TO_PROC Creates a proc which will call a method on an object call_to_s = :to_s.to_proc # Looks something like this... proc do |obj, *args| obj.send(:to_s, *args) end # Ends up being 10.send(:to_s) or 10.to_s call_to_s.call(10) # => "10" # Ends up being 10.send(:to_s, 16) or 10.to_s(16) call_to_s.call(10, 16) # => "a"
  8. 8. ASIDE:SYMBOL#TO_PROC In a method call, &:sym is a shortcut for :sym.to_proc def apply_block_to_array_and_print(&block) yield ['h', 'e', 'll', 'o', ' ', 'fun', 'ctions', '!'] end apply_block_to_array_and_print(&:join) # => "hello functions!"
  9. 9. FILTER Problem: A list needs to be filtered
  10. 10. FILTER Solution: delete everything else # Find all of the adverbs in a word list word_list.each do |item| word_list.delete(item) unless /ly$/.match(item) end
  11. 11. FILTER Better solution: build a new list! # Find all of the adverbs in a word list adverbs = [] word_list.each do |item| adverbs << item if /ly$/.match(item) end
  12. 12. FILTER Better yet: use Enumerable#select # Find all of the adverbs and non-adverbs in a word list adverbs = word_list.select { |item| /ly$/.match(item) } not_adverbs = word_list.reject { |item| /ly$/.match(item) }
  13. 13. MAP Problem: A list needs to have elements modified
  14. 14. MAP Solution: Overwrite the original list # Square all of our numbers (1...numbers.length).each do |index| numbers[index] **= 2 end
  15. 15. MAP Better Solution: generate a new list numbers = [1,2,3,4,5] squares = [] # Square all of the numbers numbers.each do |number| squares << number ** 2 end
  16. 16. MAP Better yet: use Enumerable#map numbers = [1, 2, 3, 4, 5] # Square all of our numbers squares = numbers.map { |number| number ** 2 } # Another way we could do it squares = numbers.each_with_object(2).map(&:**)
  17. 17. REDUCE Problem: A list needs to be transformed
  18. 18. REDUCE Solution: Iterate through the list numbers = [1, 2, 3, 4, 5] product = 1 # Alter the product iteratively numbers.each do |number| product *= number end
  19. 19. REDUCE Better solution: Use Enumerable#reduce numbers = [1, 2, 3, 4, 5] # Calculate the product of the list members product = numbers.reduce { |acc,item| acc * item } # Shorter way to do the same product = numbers.reduce(&:*)
  20. 20. ZIP Problem: Two lists need to be intertwined
  21. 21. ZIP Solution: Overwrite one of the lists a = [1, 2, 3] b = [4, 5, 6] # Intertwine list a with list b a.each_with_index do |number, index| a[index] = [number, b[index]] end
  22. 22. ZIP Better Solution: use Enumerable#zip a = [1, 2, 3] b = [4, 5, 6] # Intertwine list a with list b c = a.zip(b) # => [[1, 4], [2, 5], [3, 6]]
  23. 23. WARNING Religion ahead!
  24. 24. STATE Ruby is good at state. Mutable Implicit Hidden
  25. 25. DANGEROUSSTATE Consider: given_names = %w(Alice Bob Eve Mallory) short_names = given_names.select { |name| name.length < 5 } short_names.each { |name| puts name.upcase! } given_names[1] # => ???
  26. 26. DANGEROUSSTATE Consider: given_names = %w(Alice Bob Eve Mallory) short_names = given_names.select { |name| name.length < 5 } short_names.each { |name| puts name.upcase! } given_names[1] # => "BOB"
  27. 27. DUPTOTHERESCUE? Maybe Object#dup will help?: given_names = %w(Alice Bob Eve Mallory) safe_names = given_names.dup short_names = safe_names.select { |name| name.length < 5 } short_names.each { |name| puts name.upcase! } given_names[1] # => ???
  28. 28. DUPTOTHERESCUE? Maybe Object#dup will help?: given_names = %w(Alice Bob Eve Mallory) safe_names = given_names.dup short_names = safe_names.select { |name| name.length < 5 } short_names.each { |name| puts name.upcase! } given_names[1] # => "BOB"
  29. 29. WELP. State is difficult to manage and track Particularly as systems grow in complexity Things get more difficult with real threads (Rubinius, JRuby) Avoiding mutable state in most cases avoids this problem.
  30. 30. BATTLINGSTATE Avoiding state fits well with good style: Keep methods short and responsible for one thing Write methods with idempotence in mind When mutations seem necessary, use more functions
  31. 31. RULES:MADETOBEBROKEN Ruby exposes state to the programmer in a dangerous way Once concurrency comes into play, scary dragons emerge Avoiding mutable state helps, but can be expensive
  32. 32. PAINPOINTS Sometimes avoiding state doesn't make sense: Code runs much heavier than it could Code runs much slower than it otherwise might (GC runs)
  33. 33. PAINMANAGEMENT We can keep things from getting out of hand! Keep code which has side-effects to a minimum Isolate code which produces side-effects Don't make it easy to mutate state accidentally
  34. 34. QUESTIONS?COMMENTS?

×