Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Programming and Ruby

Functional Programming and Ruby

While Ruby is object oriented and imperative, it does have some features that allow for functional programming. This talk will compare how you would write functions in Haskell with Ruby, and then zoom in to take a close look at Ruby 2.0’s new “Lazy Enumerator” feature.

Pat Shaughnessy

March 12, 2013
Tweet

More Decks by Pat Shaughnessy

Other Decks in Technology

Transcript

  1. 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
  2. quicksort :: Ord a => [a] -> [a] quicksort []

    = [] quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater) where lesser = filter (< p) xs greater = filter (>= p) xs
  3. 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
  4. - what is “functional programming?” - higher order functions -

    lazy evaluation - memoization - resources for learning more
  5. [ 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 }
  6. 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 })
  7. [ x+1 | x <- [ x*x | x <-

    [1..10]]] =>[2, 5, 10, 17, 26, 37, 50, 65, 82, 101]
  8. [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:
  9. (1..10).collect { |x| x*x } .collect { |x| x+1 }

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

    x <- [1..]]] =>[2,5,10,17,26,37,50,65,82,101]
  12. (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]
  13. (1..Float::INFINITY).lazy.collect { |x| x*x } .collect { |x| x+1 }

    .first(10) =>[2,5,10,17,26,37,50,65,82,101]
  14. 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
  15. 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
  16. (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
  17. 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
  18. 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
  19. (map fib [0 ..] !!) Infinite, lazy list of return

    values A curried function to return the requested fib
  20. (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]
  21. `block in <main>': undefined method `[]' for #<Enumerator::Lazy: #<Enumerator::Lazy: 0..Infinity>:map>

    (NoMethodError) map memoized_fib [1..10] => [1,1,2,3,5,8,13,21,34,55]
  22. @cache = {}; @cache[1] = 1; @cache[2] = 1 def

    memoized_fib(n) @cache[n] ||= memoized_fib(n-1) + memoized_fib(n-2) end
  23. 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.