Slide 1

Slide 1 text

Functional Programming and Ruby Pat Shaughnessy http://patshaughnessy.net @pat_shaughnessy McKinsey & Co. Boston.rb March 12, 2013

Slide 2

Slide 2 text

the founding father of Ruby

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Ruby is a language designed in the following steps: * take a simple lisp language (like one prior to CL). * remove macros, s-expression. * add simple object system (much simpler than CLOS). * add blocks, inspired by higher order functions. * add methods found in Smalltalk. * add functionality found in Perl (in OO way). So, Ruby was a Lisp originally, in theory. Let's call it MatzLisp from now on. ;-) ! ! ! ! ! ! ! matz. http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/179642 Feb. 2006

Slide 5

Slide 5 text

the founding grandfathers of Ruby

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

why learn a foreign language?

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

quicksort :: Ord a => [a] -> [a] quicksort [] = [] quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater) where lesser = filter (< p) xs greater = filter (>= p) xs

Slide 13

Slide 13 text

Haskell... is a polymorphically statically typed, lazy, purely functional language, quite different from most other programming languages. The language is named for Haskell Brooks Curry, whose work in mathematical logic serves as a foundation for functional languages. Haskell is based on the lambda calculus, hence the lambda we use as a logo. http://www.haskell.org/haskellwiki/Introduction

Slide 14

Slide 14 text

- what is “functional programming?” - higher order functions - lazy evaluation - memoization - resources for learning more

Slide 15

Slide 15 text

what is “functional programming?”

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey

Slide 18

Slide 18 text

higher order functions

Slide 19

Slide 19 text

[1..10] =>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] (1..10).to_a

Slide 20

Slide 20 text

[ x*x | x <- [1..10]] (1..10).collect { |x| x*x } =>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] (1..10).map { |x| x*x }

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

map (\x -> x*x) [1..10] (1..10).map &lambda { |x| x*x } =>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100] (1..10).map &(->(x) { x*x })

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

[ x+1 | x <- [ x*x | x <- [1..10]]] =>[2, 5, 10, 17, 26, 37, 50, 65, 82, 101]

Slide 25

Slide 25 text

[1..10] f(x) = x*x g(x) = x+1 [2..101] f(1) = 1 g(1) = 2 Step 1: f(2) = 4 g(4) = 5 Step 2: f(3) = 9 g(9) = 10 Step 3: f(4) = 16 g(16) = 17 Step 4:

Slide 26

Slide 26 text

(1..10).collect { |x| x*x } .collect { |x| x+1 } =>[2, 5, 10, 17, 26, 37, 50, 65, 82, 101]

Slide 27

Slide 27 text

what’s happening inside of Ruby?

Slide 28

Slide 28 text

(1..10).collect { |x| x*x } each Range Enumerable #collect Enumerable#collect

Slide 29

Slide 29 text

x*x [1..100] yield loop: 1 -> 10 collect_i (1..10).collect { |x| x*x } Enumerable#collect

Slide 30

Slide 30 text

Enumerable#collect def collect(range, &block) result = [] range.each do |x| result << block.call(x) end result end

Slide 31

Slide 31 text

each Range Enumerable #collect each Enumerable #collect Enumerable#collect chain

Slide 32

Slide 32 text

[1..10] f(x) = x*x [1..100] g(x) = x+1 [2..101] Step 1: Call f(x) 10 times Step 2: Call g(x) 10 times

Slide 33

Slide 33 text

lazy evaluation

Slide 34

Slide 34 text

[1..] =>[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28, 29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54, 55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, 81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,1 05,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123, etc...

Slide 35

Slide 35 text

take 10 [1..] =>[1,2,3,4,5,6,7,8,9,10]

Slide 36

Slide 36 text

take 10 [ x+1 | x <- [ x*x | x <- [1..]]] =>[2,5,10,17,26,37,50,65,82,101]

Slide 37

Slide 37 text

(1..Float::INFINITY).lazy.collect { |x| x*x } .collect { |x| x+1 } .take(10).force =>[2,5,10,17,26,37,50,65,82,101]

Slide 38

Slide 38 text

(1..Float::INFINITY).lazy.collect { |x| x*x } .collect { |x| x+1 } .first(10) =>[2,5,10,17,26,37,50,65,82,101]

Slide 39

Slide 39 text

what’s happening inside of Ruby?

Slide 40

Slide 40 text

fib = Enumerator.new do |yielder| a = b = 1 loop do yielder << a a, b = b, a + b end end p fib.take(10) # => [1, 1, 2, 3, 5, 8... Enumerator object http://ruby-doc.org/core-2.0/Enumerator.html

Slide 41

Slide 41 text

Enumerator object Enumerator [1..100] block yielder

Slide 42

Slide 42 text

Enumerable#collect def collect(range, &block) result = [] range.each do |x| result << block.call(x) end result end

Slide 43

Slide 43 text

def lazy_collect(range, &block) Enumerator.new do |yielder| range.each do |value| yielder.yield(block.call(value)) end end end Lazy::Enumerator object: hard coded block inside Ruby http://code.google.com/p/tokland/wiki/RubyFunctionalProgramming

Slide 44

Slide 44 text

(1..10).lazy.collect { |x| x*x } each Range Enumerable #collect Enumerator::Lazy#collect

Slide 45

Slide 45 text

Enumerator::Lazy#collect Enumerator creates loop: 1 -> 10 lazy_map (1..10).lazy.collect { |x| x*x }.first(10)

Slide 46

Slide 46 text

Enumerator::Lazy#collect Enumerator loop: 1 -> 10 (1..10).lazy.collect { |x| x*x }.first(10) x*x yields once [1..100] returns

Slide 47

Slide 47 text

(1..Float::INFINITY).lazy.collect { |x| x*x } .collect { |x| x+1 }.first(10) yield to the blocks, one at a time Enumerator Enumerator Stop After 10 yield pass result pass result x*x x+1 [2..101] store yield yield loop

Slide 48

Slide 48 text

memoization

Slide 49

Slide 49 text

slow_fib 0 = 0 slow_fib 1 = 1 slow_fib n = slow_fib (n-2) + slow_fib (n-1) map slow_fib [1..10] => [1,1,2,3,5,8,13,21,34,55] http://www.haskell.org/haskellwiki/Memoization

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

memoized_fib = (map fib [0 ..] !!) where fib 0 = 0 fib 1 = 1 fib n = memoized_fib (n-2) + memoized_fib (n-1) Typical Haskell magic! http://www.haskell.org/haskellwiki/Memoization

Slide 52

Slide 52 text

(map fib [0 ..] !!) Infinite, lazy list of return values A curried function to return the requested fib

Slide 53

Slide 53 text

[0 ..] (0..Float::INFINITY)

Slide 54

Slide 54 text

map fib [0 ..] (0..Float::INFINITY).lazy.map {|x| fib(x) }

Slide 55

Slide 55 text

(map fib [0 ..] !!) cache = (0..Float::INFINITY).lazy.map {|x| fib(x) } nth_element_from_list = lambda { |ary, n| ary[n]} nth_fib = nth_element_from_list.curry[cache]

Slide 56

Slide 56 text

`block in ': undefined method `[]' for #:map> (NoMethodError) map memoized_fib [1..10] => [1,1,2,3,5,8,13,21,34,55]

Slide 57

Slide 57 text

what’s happening inside of Ruby?

Slide 58

Slide 58 text

(0..Float::INFINITY).lazy.map {|x| fib(x) } nth_element_from_list = lambda { |ary, n| ary[n]} each Range Enumerable #collect

Slide 59

Slide 59 text

@cache = {}; @cache[1] = 1; @cache[2] = 1 def memoized_fib(n) @cache[n] ||= memoized_fib(n-1) + memoized_fib(n-2) end

Slide 60

Slide 60 text

What have we learned? - Ruby has a lot of functional features! Ruby 2.0 has even more. - Ruby and Haskell can resemble each other... on the surface. - However, under the hood Ruby’s support of functional programming is limited.

Slide 61

Slide 61 text

resources for learning more

Slide 62

Slide 62 text

http://landoflisp.com

Slide 63

Slide 63 text

http://learnyouahaskell.com

Slide 64

Slide 64 text

https://leanpub.com/fp-oo

Slide 65

Slide 65 text

http://www.kickstarter.com/projects/1225193080/the-ruby-20-walkthrough

Slide 66

Slide 66 text

http://rubysource.com/functional-programming-techniques- with-ruby-part-i/ http://code.google.com/p/tokland/wiki/ RubyFunctionalProgramming

Slide 67

Slide 67 text

Thanks! Pat Shaughnessy http://patshaughnessy.net @pat_shaughnessy