Slide 1

Slide 1 text

Ruby Vs. The Titans of FP Cassandra Cruz (@celkamada)

Slide 2

Slide 2 text

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!

Slide 3

Slide 3 text

Vs.

Slide 4

Slide 4 text

What is Functional Programming?

Slide 5

Slide 5 text

Functional Programming (to me) A programming paradigm that focuses on functions as transformations on generic data, as opposed to the modeling of objects.

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Why is that a good thing? ● Easier to reuse logic ● Can compose smaller units of logic together ● Can result in cleaner, more performant code

Slide 8

Slide 8 text

What are the key features of a FP Language? ● Higher-order functions ● Currying ● Composition ● Functional Purity ● Immutability

Slide 9

Slide 9 text

What are we going to look at today? ● Higher-order functions (vs Clojure) ● Currying (vs Haskell) ● Composition (vs Javascript) ● Functional Purity ● Immutability

Slide 10

Slide 10 text

Clojure

Slide 11

Slide 11 text

What it Clojure? Born: 2007 Family: Lisp What makes it a functional language: ● Higher-order functions ● First-class functions ● Immutable Data Structures

Slide 12

Slide 12 text

Example of a function call in Clojure (function argument1 argument2 argument3)

Slide 13

Slide 13 text

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) ))))))

Slide 14

Slide 14 text

Map In Clojure (map function collection)

Slide 15

Slide 15 text

Map In Clojure (map function collection) Function Arguments

Slide 16

Slide 16 text

Map In Clojure (map function collection) Transform function Ex: increment The list of items we’re transforming Ex: [1,2,3]

Slide 17

Slide 17 text

Map increment over 1-5 (map inc [1 2 3 4 5]) ;; returns (2 3 4 5 6)

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Can we use this in Ruby? YES!

Slide 20

Slide 20 text

Map in Ruby map = proc { }

Slide 21

Slide 21 text

Map in Ruby map = proc { |function, collection| }

Slide 22

Slide 22 text

Map in Ruby map = proc { |function, collection| collection.map(&function) }

Slide 23

Slide 23 text

Map in Ruby map = proc { |f, coll| coll.map(&f) }

Slide 24

Slide 24 text

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`

Slide 25

Slide 25 text

A more complex example invalidateLogin = proc { |login| login.deleted_at = Time.now } logins = [login1, login2, login3] map.(invalidateLogins, logins)

Slide 26

Slide 26 text

Haskell

Slide 27

Slide 27 text

What is Haskell? Born: 1990 Family: ML What makes it a functional language: ● Curried Functions ● Pure Functions ● Type Inference

Slide 28

Slide 28 text

What does a function call look like in Haskell? inc = (+) 1 -- our new inc function map inc [2,3,4] -- returns [3,4,5]

Slide 29

Slide 29 text

Wait, what was with that inc?

Slide 30

Slide 30 text

Hindley-Milner Type System ● Can express the signatures of a function ● Can be used in Haskell to annotate types to be checked

Slide 31

Slide 31 text

An Example: Add (As we’re used to it) Add :: (a, a) -> a Function Name Arguments Function Result

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

What Happened in our inc function inc = (+) 1

Slide 34

Slide 34 text

What actually happened to inc (+) :: a -> a -> a Pass in 1 here

Slide 35

Slide 35 text

What actually happened to inc inc :: a -> a

Slide 36

Slide 36 text

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.

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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]

Slide 43

Slide 43 text

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!

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Javascript

Slide 46

Slide 46 text

What is Javascript? Born: 1995 Family: Javascript What makes it a functional language: ● First-class Functions ● Closures ● Strong FP Libraries

Slide 47

Slide 47 text

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.

Slide 48

Slide 48 text

FP Unfriendliness Example: JS var arr = [1,2,3] arr.map((x) => x + 1) // map is a method arr.pop // mutates ;-;

Slide 49

Slide 49 text

...wait, doesn’t Ruby do those same things?

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

How do JS programmers fix this? Functional Libraries ● Underscore ● Lodash ● Ramda

Slide 52

Slide 52 text

My favorite thing in Ramda? Compose

Slide 53

Slide 53 text

Have you ever done this? def fn (x) var1 = fn2(x) var2 = fn3(var1) var3 = fn4(var2) # 30 lines later... fn34(var33) end

Slide 54

Slide 54 text

Compose to the rescue! import R from 'ramda' var add2 = R.compose( R.add(1), R.add(1) ) add2(2) // returns 4

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

Benefits of Compose

Slide 57

Slide 57 text

Can Reuse Logic Easily const inc = add(1) const dec = add(-1) const c1 = compose(inc, dec) const c2 = compose(dec, inc)

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

What if we just want a gem? require ‘reductio’ inc = Reductio::Add.(1) add3 = Reductio::Compose.(inc, inc, inc) add3.(4) # returns 7

Slide 67

Slide 67 text

What have we established Ruby can do? ● Higher Order Functions ● Composition ● Currying

Slide 68

Slide 68 text

The tools we have are enough!

Slide 69

Slide 69 text

Vs.

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

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