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

Ruby Vs. The Titans Of FP

Ruby Vs. The Titans Of FP

Clojure, Haskell and Javascript reign as the dominant functional languages of our era. But surely anything they can do, Ruby can do better? And what is it that they actually do? Come learn about three core concepts of functional programming, and see how Ruby stacks up against its peers when it comes to making them powerful.

Cassandra Cruz

November 12, 2016
Tweet

Other Decks in Programming

Transcript

  1. Bit about me • Hobbyist programmer for a decade +

    • Fascinated with Functional Programming • Relatively new to industry (currently 8 months at Mavenlink) • Working at Mavenlink is my first real experience with Ruby Hi!
  2. Vs.

  3. Functional Programming (to me) A programming paradigm that focuses on

    functions as transformations on generic data, as opposed to the modeling of objects.
  4. Ex: OO vs. FP Login Object • Knows the state

    of a session • Knows how to validate itself Login Object • Knows the state of a session Login Library • Knows how to validate the login object
  5. Why is that a good thing? • Easier to reuse

    logic • Can compose smaller units of logic together • Can result in cleaner, more performant code
  6. What are the key features of a FP Language? •

    Higher-order functions • Currying • Composition • Functional Purity • Immutability
  7. What are we going to look at today? • Higher-order

    functions (vs Clojure) • Currying (vs Haskell) • Composition (vs Javascript) • Functional Purity • Immutability
  8. What it Clojure? Born: 2007 Family: Lisp What makes it

    a functional language: • Higher-order functions • First-class functions • Immutable Data Structures
  9. What a program ends up looking like (defn take [n

    coll] (lazy-seq (when (pos? n) (when-let [s (seq coll)] (cons (first s) (take (dec n) (rest s) ))))))
  10. Map In Clojure (map function collection) Transform function Ex: increment

    The list of items we’re transforming Ex: [1,2,3]
  11. Map increment over 1-5 (map inc [1 2 3 4

    5]) ;; returns (2 3 4 5 6)
  12. What is a Higher-Order Function? A higher order function is

    a function that takes in functions as arguments or returns another function (map function collection) function that takes a function
  13. How do we use this? inc = proc { |x|

    x + 1 } # proc that adds 1 to input nums = [2,3,4] # data for us to map over map.call(inc, nums) # returns [3,4,5] map.(inc, nums) # syntactic sugar to hide the `call`
  14. A more complex example invalidateLogin = proc { |login| login.deleted_at

    = Time.now } logins = [login1, login2, login3] map.(invalidateLogins, logins)
  15. What is Haskell? Born: 1990 Family: ML What makes it

    a functional language: • Curried Functions • Pure Functions • Type Inference
  16. What does a function call look like in Haskell? inc

    = (+) 1 -- our new inc function map inc [2,3,4] -- returns [3,4,5]
  17. Hindley-Milner Type System • Can express the signatures of a

    function • Can be used in Haskell to annotate types to be checked
  18. An Example: Add (As we’re used to it) Add ::

    (a, a) -> a Function Name Arguments Function Result
  19. An Example: Add (As Haskell does it) (+) :: a

    -> a -> a Function Name Arguments Function Result
  20. Curried Function A function that upon being applied to less

    than its total set of arguments returns another function that waits for the rest of its arguments.
  21. How to implement in Ruby add = proc { |x|

    # bind `x` using a closure }
  22. How to implement in Ruby add = proc { |x|

    proc { |y| # returns a proc that will receive `y` } }
  23. How to implement in Ruby add = proc { |x|

    proc { |y| x + y # internal proc returns x + y } }
  24. How to implement in Ruby add = proc { |x|

    proc { |y| x + y } } inc = add.(1) # binds x to 1
  25. How to implement in Ruby add = proc { |x|

    proc { |y| x + y } } inc = add.(1) inc.(2) # binds y to 2, returns 3
  26. How to implement in Ruby add = proc { |x|

    proc { |y| x + y } } inc = add.(1) inc.(2) map.(inc, [1,2,3]) # returns [2,3,4]
  27. How to implement in Ruby(the easy way) add = proc

    { |x, y| x + y }.curry inc = add.(1) inc.(2) # returns 3 add.(2, 3) # returns 5 map.(inc, [2,3,4]) # returns [3,4,5] Native Currying!
  28. On the order of arguments inc = add.(1) inc_map =

    map.(inc) nums = [1,2,3,4,5] inc_map.(nums) #returns [2,3,4,5,6] dec = add.(-1) dec_map = map.(dec) nums = [1,2,3,4,5] dec_map.(nums) #returns [0,1,2,3,4] Currying lets us build entire families of functions
  29. What is Javascript? Born: 1995 Family: Javascript What makes it

    a functional language: • First-class Functions • Closures • Strong FP Libraries
  30. How well does JS support FP? Despite the prevalence of

    FP concepts in JS, the core API doesn't really cater well to programming in a functional way in some places.
  31. FP Unfriendliness Example: JS var arr = [1,2,3] arr.map((x) =>

    x + 1) // map is a method arr.pop // mutates ;-;
  32. FP Unfriendliness Example: Ruby arr = [1,2,3] arr.map{ |x| x

    + 1 } // map is a method arr.pop // mutates ;-;
  33. Have you ever done this? def fn (x) var1 =

    fn2(x) var2 = fn3(var1) var3 = fn4(var2) # 30 lines later... fn34(var33) end
  34. Compose to the rescue! import R from 'ramda' var add2

    = R.compose( R.add(1), R.add(1) ) add2(2) // returns 4
  35. A MapReduce function import R from 'ramda' // incAdd ::

    [a] -> b // R.reduce :: ((a, b) -> a) -> a -> [b] -> a // R.map :: (a -> b) -> [a] -> [b] var incAdd = R.compose( R.reduce(R.add, 0), R.map(R.inc) ) incAdd([1,2,3,4,5]) // returns 20
  36. Can Reuse Logic Easily const inc = add(1) const dec

    = add(-1) const c1 = compose(inc, dec) const c2 = compose(dec, inc)
  37. Can Compose Compositions const inc = add(1) const dec =

    add(-1) const c1 = compose(inc, dec) const c2 = compose(c1, inc)
  38. How do we do this in Ruby? compose = proc

    { |x, y| # two functions to compose }
  39. How do we do this in Ruby? compose = proc

    { |x, y| Proc{ |*args| # returns proc that takes in args } }
  40. How do we do this in Ruby? compose = proc

    { |x, y| Proc{ |*args| x.(y.(*args)) # invoke functions with the args } }
  41. How do we do this in Ruby? compose = proc

    { |x, y| Proc{ |*args| x.(y.(*args)) } } inc = add.(1) # create our friendly inc function
  42. How do we do this in Ruby? compose = proc

    { |x, y| Proc{ |*args| x.(y.(*args)) } } inc = add.(1) add2 = compose.(inc, inc) # compose `inc` with itself
  43. How do we do this in Ruby? compose = proc

    { |x, y| Proc{ |*args| x.(y.(*args)) } } inc = add.(1) add2 = compose.(inc, inc) add2.(4) # returns 6
  44. What if we want more than two functions? v_compose =

    proc { |*funcs| funcs.reduce(&compose) } add3 = v_compose.(inc, inc, inc) add3.(4) # returns 7
  45. What if we just want a gem? require ‘reductio’ inc

    = Reductio::Add.(1) add3 = Reductio::Compose.(inc, inc, inc) add3.(4) # returns 7
  46. What have we established Ruby can do? • Higher Order

    Functions • Composition • Currying
  47. Vs.

  48. Cassandra Cruz Github: lambdatastic Twitter: Celkamada Email: [email protected] mavenlink.com/engineering Next

    Steps: • Dr. Frisby’s Mostly Adequate Guide to Functional Programming • github.com/lambdatastic/Reductio.rb • Tweet with #functionalruby for discussion • Tweet questions at me • Help with Reductio? Please? discord.me/transcord