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

Functional Programming Concepts

Rick Liu
September 17, 2017

Functional Programming Concepts

RubyConf China 2017

Rick Liu

September 17, 2017
Tweet

More Decks by Rick Liu

Other Decks in Programming

Transcript

  1. CHURCH ENCODING Booleans, integers, (and other data structures) can be

    entirely replaced by lambda calculus! https://en.wikipedia.org/wiki/Church_encoding
  2. zero one two true false λf . λx. x λf

    . λx. f (x) λf . λx. f (f (x) ⋮ λx. λy. x λx. λy. y
  3. CHURCH THESIS Anything that is effectively computable can be computed

    by -Calculus λ https://en.wikipedia.org/wiki/Church%E2%80%93Turing_thesis
  4. Can Programming Be Liberated from the von Neumann Style? A

    Functional Style and Its Algebra of Programs John Backus IBM Research Laboratory, San Jose Turing award 1977 https://news.ycombinator.com/item?id=7671379
  5. Functions are definitions f (x) = { x 2 2x

    + 1 if x is even otherwise
  6. FP CONCEPTS Functions as first class citizens Higher order functions

    Pure functions Lists and recursion Lazy evaluation Monads
  7. VALUES They can be created by the running program They

    can be assigned to variables They can be passed into functions They can be returned by functions
  8. PROC add = Proc.new { |x, y| x + y

    } # Kernel#proc add = proc { |x, y| x + y } # Kernel#lambda add = lambda { |x, y| x + y } # => #Proc:0x0000010299a1d0 (lambda) # stabby lambda add = -> x, y { x + y }
  9. BLOCK anonymous function def capture_block(&block) # a new proc is

    created block.call end capture_block { puts "Inside the block" }
  10. METHOD AS OBJECT class Cat def talk puts "self is

    #{self}" end end c = Cat.new # => #Cat:0x00007faf60008888 m = c.method(:talk) # => #Method: Cat#talk m.call # => self is #Cat:0x00007faf60008888 http://ruby-doc.org/core/Object.html#method-i-method
  11. CALLING CALLABLE OBJECTS add = -> x, y { x

    + y } add.call(1, 2) # => 3 add.(1, 2) # => 3 add[1, 2] # => 3
  12. Symbol#to_proc class Symbol def to_proc # naive implementation -> obj

    { obj.send(self) } end end http://ruby-doc.org/core/Symbol.html#method-i-to_proc
  13. Generalizing #to_proc class Person < Struct.new(:name) def self.to_proc -> person

    { person.name } end end [Person.new("Jack"), Person.new("Rick")].map &Person # => ["Jack", "Rick"]
  14. FUNCTION OBJECT class ColorFilter < Struct.new(:color) def to_proc -> item

    { item.color == self.color } end def call(list) list.select &self end end red_filter = ColorFilter.new(:red) red_filter.call(color_list) == color_list.select(&red_filter)
  15. FP CONCEPTS Functions as first class citizens Higher order functions

    Pure functions Lists and recursion Lazy evaluation Monads
  16. HIGHER-ORDER FUNCTION takes one or more functions as arguments or

    returns a function as its result https://en.wikipedia.org/wiki/Higher-order_function
  17. FILTER filter = -> f, list { list.select &f }

    even = -> x { x % 2 == 0 } filter.(even, (1..10)) # => [2, 4, 6, 8, 10]
  18. MAP map = -> f, list { list.map &f }

    double = -> x { x * 2 } map.(double, (1..5)) # => [2, 4, 6, 8, 10]
  19. FOLD fold = -> f, init, list { list.reduce(init) {

    |a, b| f.(a, b) } } plus = -> x, y { x + y } fold.(plus, 0, (1..5)) # => 15
  20. food = -> item { item.category == :food } price

    = -> item { item.price } fold(plus, 0, map(price, filter(food, items)))
  21. FUNCTION COMPOSITION f (x) = x + 1 g(x) =

    x 2 f (g(2)) = 5 g(f (2)) = 9 f ∘ g(x) = + 1 x 2 g ∘ f (x) = (x + 1) 2
  22. COMPOSE OPERATOR class Proc def ◦(other) -> (*args) { self.(other.(*args))

    } end end even = -> x { x % 2 == 0 } square = -> x { x * x } add1 = -> x { x + 1 } # is (x + 1)^2 even? f = even.◦ square.◦ add1 f[1] # => true f[2] # => false
  23. make_adder = -> x { -> y { x +

    y } } add1 = make_adder[1] add2 = make_adder[2] add1[3] # => 4 add2[3] # => 5
  24. Proc#curry add = -> x, y, z { x +

    y + z } a = add.curry[1] a[2, 3] # => 6 a[2][3] # => 6 add.curry[1, 2][3] # => 6 add.curry[1][2][3] # => 6 http://ruby-doc.org/core/Method.html#method-i-curry
  25. make_counter = -> amount { count = 0 -> {

    count += amount } } a = make_counter.(1) a.() # => 1 a.() # => 2 b = make_counter.(5) b.() # => 5 a.() # => 3
  26. FP CONCEPTS Functions as first class citizens Higher order functions

    Pure functions Lists and recursion Lazy evaluation Monads
  27. PURE FUNCTION Data in, data out No side effects parameters

    → f unction → result https://en.wikipedia.org/wiki/Pure_function
  28. These functions aren't pure: def f(x) global_a = global_a +

    x end def h(x) print x + 1 end def g(x) x + read_y end
  29. REFERENTIAL TRANSPARENCY A expression can be replaced with its corresponding

    value without changing the program's behavior. https://en.wikipedia.org/wiki/Referential_transparency
  30. Referentially transparent fact = -> n { n == 0

    ? 1 : n * fact.(n - 1) } fact[0] # => 1 fact[1] # => 1 = 1 * fact[0] = 1 * 1 fact[2] # => 2 = 2 * fact[1] = 2 * 1 fact[3] # => 6 = 3 * fact[2] = 3 * 2
  31. Referentially opaque print next_random() + next_random() # => 1 +

    6 = 7 x = next_random() # => 2 print x + x # => 2 + 2 = 4
  32. IMMUTABILITY A name is immutable if the name's value can't

    change. a = 3 double_a = 2 * a https://en.wikipedia.org/wiki/Immutable_object
  33. # Not OK x = x + 1 total =

    0 orders.each { |order| # Not OK total = total + order.amount } print total
  34. IMMUTABLE OBJECT state cannot be modified a er it is

    created class Counter attr_reader :count def initialize(count: 0) @count = count end def inc self.class.new(count: count + 1) end end
  35. BENEFITS OF PURE FUNCTIONS Can be reused without regard to

    context Easier to test and debug, reduce setup Avoid race condition and achieve thread safety Memoization
  36. No Memoization fib = -> n { if n ==

    0 || n == 1 n else fib.(n-1) + fib.(n-2) end } (1..10).map &fib # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
  37. Controlled Memoization fib = -> (n, memo = {}) {

    if n == 0 || n == 1 n else memo[n] ||= fib.(n-1) + fib.(n-2) end } (1..10).map &fib # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
  38. Typical Haskell magic! memoized_fib = (map fib [0..] !!) where

    fib 0 = 0 fib 1 = 1 fib n = memoized_fib(n-2) + memoized_fib(n-1) https://wiki.haskell.org/Memoization
  39. FP CONCEPTS Functions as first class citizens Higher order functions

    Pure functions Lists and recursion Lazy evaluation Monads
  40. LIST Sequence of elements of the same type Possibly empty

    [] Possibly finite [5,13,7] Possibly infinite [0,2,4,6,8,10,12,...] https://en.wikipedia.org/wiki/List_(abstract_data_type)
  41. BASIC LIST OPERATIONS head : list -> value head([1,2,3]) =

    1 tail : list -> list tail([1,2,3]) = [2,3] construct : value, list -> list construct(1, [2,3]) = [1,2,3] concatenate : list, list -> list concatenate([1,2], [8,10]) = [1,2,8,10]
  42. CONSTRUCT construct : value, list -> list 3::[5,4,2,1] = [3,5,4,2,1]

    8::[] = [8] h::t = [5,7,9,10] h = 5 t = [7,9,10]
  43. Quciksort: functional style quicksort([]) = [] quicksort(h::t) = concatenate (

    quicksort(filter(( x -> x < h), t)), [h], quicksort(filter(( x -> x >= h), t)) )
  44. FP CONCEPTS Functions as first class citizens Higher order functions

    Pure functions Lists and recursion Lazy evaluation Monads
  45. LAZY EVALUATION or call-by-need, is an evaluation strategy which delays

    the evaluation of an expression until its value is needed (non-strict evaluation) and which also avoids repeated evaluations https://en.wikipedia.org/wiki/Lazy_evaluation
  46. Enumerator::lazy (1..Float::INFINITY) .lazy .map { |i| i * i }

    .first(10) # => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] http://ruby-doc.org/core/Enumerable.html#method-i-lazy http://ruby-doc.org/core/Enumerator/Lazy.html
  47. Use wrap function to achieve lazy evaluation wrapped_value = ->

    { slow_expression } # call-by-need wrapped_value.()
  48. Lazy evaluation in Haskell take 10 [1..] # => [1,2,3,4,5,6,7,8,9,10]

    take 10 [ x+2 | x <- [ x*x | x <- [1..]]] # => [3,6,11,18,27,38,51,66,83,102] https://wiki.haskell.org/Lazy_evaluation
  49. FP CONCEPTS Functions as first class citizens Higher order functions

    Pure functions Lists and recursion Lazy evaluation Monads
  50. f (x) = − 8 1 − 4 x +

    6 ‾ ‾ ‾‾‾ √ error = nil if x >= -6 y = sqrt(x + 6) - 4 if y == 0 error = true else z = 1 / y - 8 end else error = true end
  51. MAYBE MONAD sqrtMaybe : float -> Maybe a float sqrtMaybe(x)

    = if x >= 0 Just sqrt(x) else Nothing
  52. FUNCTION COMPOSITION If you have Just y, apply f to

    y, getting Just f(y) or Nothing, if you have Nothing, get Nothing bind : Maybe a float, f -> Maybe a float
  53. bind ( Just 4, sqrtMaybe ) # => Just 2

    bind ( Just -1, sqrtMaybe ) # => Nothing bind ( Nothing, sqrtMaybe ) # => Nothing
  54. f (x) = − 8 1 − 4 x +

    6 ‾ ‾ ‾‾‾ √ bind( bind( bind(sqrtMaybe(x+6), minus4Maybe), reciprocalMaybe), minus8Maybe)
  55. MONAD 1. Define a data type, and rules of values

    2. Create functions use the data type 3. Compose functions into actions with rules in #1 https://en.wikipedia.org/wiki/Monad_(functional_programming)
  56. FP CONCEPTS Functions as first class citizens Higher order functions

    Pure functions Lists and recursion Lazy evaluation Monads
  57. FUNCTIONAL MINDSET Data pipelines Composition Pure functions first Seperate side

    effects Immutable value objects Callable function objects Functional Abstractions