A Gentle Introduction to Functional Paradigms in Ruby
-
Upload
callmewuest -
Category
Technology
-
view
204 -
download
1
description
Transcript of A Gentle Introduction to Functional Paradigms in Ruby
![Page 1: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/1.jpg)
FUNCTIONAL RUBY
![Page 2: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/2.jpg)
FIRST-CLASS FUNCTIONS
You already use these# Print each number from 1-30 in hexadecimal(1..30).each do |number| puts number.to_s(16)end
![Page 3: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/3.jpg)
FIRST-CLASS FUNCTIONS
Changing the example slightly:# Create a lambda which prints a number in hexadecimalputs_in_hex = lambda do |number| puts number.to_s(16)end
(1..10).each { |i| puts_in_hex.call(i) }
![Page 4: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/4.jpg)
FIRST-CLASS FUNCTIONS
This can be further simplified:puts_in_hex = lambda do |number| puts number.to_s(16)end
(1..10).each(&puts_in_hex)
![Page 5: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/5.jpg)
ASIDE: THE AMPERSAND
The ampersand indicates that we're working with a blockdef use_block(&block) block.call(2)end
use_block do |number| number * 2end# => 4
![Page 6: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/6.jpg)
ASIDE: THE AMPERSAND
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 * 2end
use_block(&multiply_two)# => 4
![Page 7: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/7.jpg)
ASIDE: SYMBOL#TO_PROC
Creates a proc which will call a method on an objectcall_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_scall_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"
![Page 8: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/8.jpg)
ASIDE: SYMBOL#TO_PROC
In a method call, &:sym is a shortcut for :sym.to_procdef apply_block_to_array_and_print(&block) yield ['h', 'e', 'll', 'o', ' ', 'fun', 'ctions', '!']end
apply_block_to_array_and_print(&:join)# => "hello functions!"
![Page 9: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/9.jpg)
FILTERProblem: A list needs to be filtered
![Page 10: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/10.jpg)
FILTERSolution: delete everything else
# Find all of the adverbs in a word listword_list.each do |item| word_list.delete(item) unless /ly$/.match(item)end
![Page 11: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/11.jpg)
FILTERBetter solution: build a new list!
# Find all of the adverbs in a word listadverbs = []word_list.each do |item| adverbs << item if /ly$/.match(item)end
![Page 12: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/12.jpg)
FILTERBetter yet: use Enumerable#select
# Find all of the adverbs and non-adverbs in a word listadverbs = word_list.select { |item| /ly$/.match(item) }not_adverbs = word_list.reject { |item| /ly$/.match(item) }
![Page 13: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/13.jpg)
MAPProblem: A list needs to have elements modified
![Page 14: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/14.jpg)
MAPSolution: Overwrite the original list
# Square all of our numbers(1...numbers.length).each do |index| numbers[index] **= 2end
![Page 15: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/15.jpg)
MAPBetter Solution: generate a new list
numbers = [1,2,3,4,5]squares = []
# Square all of the numbersnumbers.each do |number| squares << number ** 2end
![Page 16: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/16.jpg)
MAPBetter yet: use Enumerable#map
numbers = [1, 2, 3, 4, 5]
# Square all of our numberssquares = numbers.map { |number| number ** 2 }
# Another way we could do itsquares = numbers.each_with_object(2).map(&:**)
![Page 17: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/17.jpg)
REDUCEProblem: A list needs to be transformed
![Page 18: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/18.jpg)
REDUCESolution: Iterate through the list
numbers = [1, 2, 3, 4, 5]product = 1
# Alter the product iterativelynumbers.each do |number| product *= numberend
![Page 19: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/19.jpg)
REDUCEBetter solution: Use Enumerable#reduce
numbers = [1, 2, 3, 4, 5]
# Calculate the product of the list membersproduct = numbers.reduce { |acc,item| acc * item }
# Shorter way to do the sameproduct = numbers.reduce(&:*)
![Page 20: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/20.jpg)
ZIPProblem: Two lists need to be intertwined
![Page 21: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/21.jpg)
ZIPSolution: Overwrite one of the lists
a = [1, 2, 3]b = [4, 5, 6]
# Intertwine list a with list ba.each_with_index do |number, index| a[index] = [number, b[index]]end
![Page 22: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/22.jpg)
ZIPBetter Solution: use Enumerable#zip
a = [1, 2, 3]b = [4, 5, 6]
# Intertwine list a with list bc = a.zip(b)# => [[1, 4], [2, 5], [3, 6]]
![Page 23: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/23.jpg)
WARNINGReligion ahead!
![Page 24: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/24.jpg)
STATERuby is good at state.
Mutable
Implicit
Hidden
![Page 25: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/25.jpg)
DANGEROUS STATE
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] # => ???
![Page 26: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/26.jpg)
DANGEROUS STATE
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"
![Page 27: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/27.jpg)
DUP TO THE RESCUE?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] # => ???
![Page 28: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/28.jpg)
DUP TO THE RESCUE?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"
![Page 29: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/29.jpg)
WELP.State is difficult to manage and trackParticularly as systems grow in complexityThings get more difficult with real threads (Rubinius, JRuby) Avoiding mutable state in most cases avoids this problem.
![Page 30: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/30.jpg)
BATTLING STATEAvoiding state fits well with good style:
Keep methods short and responsible for one thingWrite methods with idempotence in mindWhen mutations seem necessary, use more functions
![Page 31: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/31.jpg)
RULES: MADE TO BE BROKEN
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
![Page 32: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/32.jpg)
PAIN POINTS
Sometimes avoiding state doesn't make sense:Code runs much heavier than it couldCode runs much slower than it otherwise might (GC runs)
![Page 33: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/33.jpg)
PAIN MANAGEMENTWe can keep things from getting out of hand!
Keep code which has side-effects to a minimumIsolate code which produces side-effectsDon't make it easy to mutate state accidentally
![Page 34: A Gentle Introduction to Functional Paradigms in Ruby](https://reader034.fdocuments.net/reader034/viewer/2022052622/558cc1c3d8b42afe198b45e5/html5/thumbnails/34.jpg)
QUESTIONS? COMMENTS?