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

Functional Programming Principles in Ruby

Functional Programming Principles in Ruby

In this talk, I present some functional programming principles and give some examples of how you could use functional programming in Ruby.

Matheus

May 25, 2019
Tweet

More Decks by Matheus

Other Decks in Programming

Transcript

  1. About me • Matheus Mina • Backend Developer @ XING

    • Working with Ruby since 2012 • @mfbmina
  2. What is? • FP is a paradigm that treats the

    computation as a resolution of mathematical functions • Avoids changes of state and data mutability • Has its origins from lambda calculus, in 1930 • LISP, 1950s • Clojure, Erlang, Elixir…. And now Ruby ❤
  3. Principles • First-class functions & High-order functions • Pure functions

    • Immutability • Closures • Currying • Function composition • Pattern Matching
  4. Functions == first class citizens • A language has first-class

    functions if it is possible to use functions as the same way that we use primitive types. • It means that is possible send a function as an argument, return a function, etc.
  5. f(x) = x 1. It must return the same value

    if it has the same arguments 2. The function evaluation must have zero side effects. No mutation.
  6. @value = 3 def square @value**@value end puts square def

    square(value) value**value end puts square(3) = 9
  7. def greetings(name) string = 'Hello' string << name end def

    greetings(name) string = 'Hello ' string + name end
  8. Strong x Weak immutability def f @f ||= 1 #

    expensive operation... end
  9. Strings • Strings and other objects are typically expressed as

    immutable objects to improve readability and runtime efficiency. • Ruby way: • # frozen_string_literal: true • ruby --enable-frozen-string-literal your_file.rb
  10. Strings name = 'Matheus' name.upcase! #=> RuntimeError: can't modify frozen

    String x = String.new('Matheus') x.upcase! puts x #=> 'MATHEUS' y = 'Matheus'.dup y.upcase! puts y #=> ‘MATHEUS'
  11. How to run this “variable”? class Array def my_each(&block) i

    = 0 while i < size yield at(i) i += 1 end end end
  12. Blocks # Blocks are a chunk of code that can

    be passed to an object/method and are executed under the context of that object. [1, 2, 3, 4, 5].my_each { |element| puts element }
  13. Procs # proc is an instance of Proc, which holds

    a code block to be executed my_proc = Proc.new { |element| puts element } my_proc.call(100) [1, 2, 3, 4, 5].my_each(&my_proc)
  14. Lambdas # lambda is a way to define a block

    and its parameters with some special syntax my_lambda = ->(element) { puts element } my_lambda.call(100) [1, 2, 3, 4, 5].my_each(&my_lambda)
  15. Lambdas x Procs f = ->(a) { puts a }

    f.call(1, 2) # ArgumentError (wrong number of arguments (given 2, expected 1)) def foo f = Proc.new { return "return from proc" } f.call # control leaves foo here return "return from foo" end def bar f = lambda { return "return from lambda" } f.call # control does not leave bar here return "return from bar" end puts foo # => "return from proc" puts bar # => "return from bar"
  16. Methods # When using the function `method`, it makes the

    function executable from another function def my_method(element) puts element end [1, 2, 3, 4, 5].my_each(&method(:my_method))
  17. f(x, y) == g(x)(y) # currying is the technique of

    translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. add = ->(x, y, z) { x + y + z} puts add.curry.(1).(2).(3) # Currying is related to, but not the same as, partial function. add_curry = add.curry partial_function = add_curry.(1).(2) z = 3 # another expensive operation… puts partial_function.(z)
  18. h(x) = g(f(x)) def f(x) x + 1 end def

    g(y) y + 2 end def h(z) z + 3 end puts h(g(f(1)))
  19. Using procs / lambdas f = ->(x) { x +

    1 } g = ->(y) { y + 2 } h = ->(z) { z + 3 } resp = f >> g >> h puts resp.call(1)
  20. Coming soon • Ruby 2.7 • (irb):24: warning: Pattern matching

    is experimental, and the behavior may change in future versions of Ruby!
  21. Act of checking a sequence of tokens for the presence

    of some pattern case 0 in 0 | 1 true end # => true case [1, 2, 3, 4, 5] in a, *b b end # => [2, 3, 4, 5] case { a: 'ofoo', b: 'fddffd' } in a: a, **h h end # => {:b=>"fddffd"}
  22. In contrast to pattern recognition, the match has to be

    exact case 10 in 0 | 1 true end # => NoMatchingPatternError
  23. • Reducing side effects • Simpler code • Ease of

    predicting behavior • Less abstractions • Tests are simpler • Thread-safe