28
+ = Ruby Functional Programming

Ruby Functional Programming

Embed Size (px)

DESCRIPTION

Ruby functional programming.

Citation preview

Page 1: Ruby Functional Programming

+ =

Ruby Functional Programming

Page 2: Ruby Functional Programming

Ruby Functional Programming

Functional Programming by Wikipidia: “Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data". In other words, functional programming promotes code with no side effects, no change of value in variables. It oposes to imperative programming, which enfatizes change of state”.

Page 3: Ruby Functional Programming

Ruby Functional Programming

What this means? ● No mutable data (no side effect). ● No state (no implicit, hidden state).

Once assigned (value binding), a variable (a symbol) does not change its value. All state is bad? No, hidden, implicit state is bad. Functional programming do not eliminate state, it just make it visible and explicit (at least when programmers want it to be). ● Functions are pure functions in the mathematical sense: their output depend only

in their inputs, there is not “environment”. ● Same result returned by functions called with the same inputs.

Page 4: Ruby Functional Programming

Ruby Functional Programming

What are the advantages? ● Cleaner code: "variables" are not modified once defined, so we don't have to

follow the change of state to comprehend what a function, a, method, a class, a whole project works.

● Referential transparency: Expressions can be replaced by its values. If we call a function with the same parameters, we know for sure the output will be the same (there is no state anywhere that would change it).

There is a reason for which Einstein defined insanity as "doing the same thing over and over again and expecting different results".

Page 5: Ruby Functional Programming

Ruby Functional Programming

Advantages enabled by referential transparence

● Memoization

○ Cache results for previous function calls.

● Idempotence ○ Same results regardless how many times you call a function.

● Modularization ○ We have no state that pervades the whole code, so we build our project with

small, black boxes that we tie together, so it promotes bottom-up programming.

● Ease of debugging

○ Functions are isolated, they only depend on their input and their output, so they are very easy to debug.

Page 6: Ruby Functional Programming

Ruby Functional Programming

Advantages enabled by referential transparence ● Parallelization

○ Functions calls are independent. ○ We can parallelize in different process/CPUs/computers/…

We can execute func1 and func2 in paralell because a won’t be modified.

result = func1(a, b) + func2(a, c)

Page 7: Ruby Functional Programming

Ruby Functional Programming

Advantages enabled by referential transparence ● Concurrence

a. With no shared data, concurrence gets a lot simpler: i. No semaphores. ii. No monitors. iii. No locks. iv. No race-conditions. v. No dead-locks.

Page 8: Ruby Functional Programming

Ruby Functional Programming

Ruby is an imperative programming language. As a Ruby programmer why uses functional programming in Ruby? Ruby is not a functional language but have a lot of features that enables us to applies functional principles in the development, turning our code more elegant, concise, maintanable, easier to understand and test.

Page 9: Ruby Functional Programming

Ruby Functional Programming

Don’t Update, Create - Strings name = “Geison” name = “#{name} Flores”

FIRSTNAME = “Geison” LASTNAME = “#{name} Flores” NAME = “#{FIRSTNAME} #{LASTNAME}”

Page 10: Ruby Functional Programming

Ruby Functional Programming

Don’t Update, Create - Arrays years = [2001, 2002] years << 2003 years += [2004, 2005] years # [2001, 2002, 2003, 2004, 2005]

YEARS = [2001, 2001] ALL_YEARS = YEARS + [2003] + [2004, 2005]

Page 11: Ruby Functional Programming

Ruby Functional Programming

Don’t Update, Create - Hashes ages = {“John” => 30} ages[“Mary”] = 28 ages # {“John” => 30, “Mary” => 28}

AGES = {“John” => 30} ALL_AGES = AGES.merge(“Mary” => 28)

Page 12: Ruby Functional Programming

Ruby Functional Programming

Immutable Objects ● An OO pattern that was originated in FP world.

● When changing a data structure, don’t modify in place but create a new object.

● In Ruby this is tipically the dafault. Methods that don’t follow this principal are assumed “dangerous” and are tipically marked with ‘!’.

○ name.reverse => returns a new string that contaings the reversed name.

○ name.reverse! => replaces the name with reversed value.

Page 13: Ruby Functional Programming

Ruby Functional Programming

Everythings is an expression if num == "one" then val = 1 elsif num == "two" then val = 2 else then val = 3 end

val = if num == "one" then 1 elsif num == "two" then 2 else 3 end

Page 14: Ruby Functional Programming

Ruby Functional Programming

# a trivial example that adds a loop control structure # it takes a range and yields the passed block. def loop(x,&b) for i in x do b.call(i) end end # use the above defined method loop(1..10) do |x| puts x end

Higher Order Functions

Functions are higher-order when they can take other functions as arguments, and return them as results. This is done in Ruby using lambda and block logic.

Page 15: Ruby Functional Programming

Ruby Functional Programming

Higher Order Functions init-empty + each + push = map

dogs = [] ["milu", "rantanplan"].each do |name| dogs << name.upcase end dogs # ["MILU", "RANTANPLAN"]

dogs = ["milu", "rantanplan"].map do |name| name.upcase end # ["MILU", "RANTANPLAN"]

Page 16: Ruby Functional Programming

Ruby Functional Programming

Higher Order Functions init-empty + each + conditional push -> select/reject

dogs = [] ["milu", "rantanplan"].each do |name| if name.size == 4 dogs << name end end dogs # ["milu"]

dogs = ["milu", "rantanplan"].select do |name| name.size == 4 end # ["milu"]

Page 17: Ruby Functional Programming

Ruby Functional Programming

Higher Order Functions initialize + each + accumulate -> inject

length = 0 ["milu", "rantanplan"].each do |dog_name| length += dog_name.length end length # 14

length = ["milu", "rantanplan"].inject(0) do |accumulator, dog_name| accumulator + dog_name.length end # 14

# In this particular case, when there is a simple operation between accumulator and element, we don't need to write the block, just pass the symbol of the binary operation and the initial value: length = ["milu", "rantanplan"].map(&:length).inject(0, :+) # 14

Page 18: Ruby Functional Programming

Ruby Functional Programming

Higher Order Functions init-empty + upto + merge arrays -> zip

xs = [1, 2, 3] ys = [:a, :b, :c] output = [] 0.upto(xs.length - 1).each do |idx| output << [xs[idx], ys[idx]] end output # [[1, :a], [2, :b], [3, :c]]

xs = [1, 2, 3] ys = [:a, :b, :c] output = xs.zip(ys) # [[1, :a], [2, :b], [3, :c]]

Page 19: Ruby Functional Programming

Ruby Functional Programming

Currying and Partial Functions Higher-order functions enable Currying, which the ability to take a function that accepts n parameters and turns it into a composition of n functions each of them take 1 parameter. A direct use of currying is the Partial Functions where if you have a function that accepts n parameters then you can generate from it one of more functions with some parameter values already filled in.

plus = lambda {|a,b| a + b} # defining a proc that sums 2 numbers plus.(3,5) #=> 8 # curring calling partial function by supplying the first parameters with value 1 plus_one = plus.curry.(1)

# I can use the new proc as normal plus_one.(5) #=> 6

Page 20: Ruby Functional Programming

Ruby Functional Programming

Eager vs Lazy Evaluation ● Eager evaluation: expressions are calculated at the moment that variables is assined,

function called...

● Lazy evaluation: delays the evaluation of the expression until it is needed.

○ Memory efficient: no memory used to store complete structures.

○ CPU efficient: no need to calculate the complete result before returning.

○ Laziness is not a requisite for FP, but it is a strategy that fits nicely on the paradigm(Haskell).

Ruby uses eager evaluation (but short-circuits && or ||).

Ruby blocks are a mechanism for lazy evaluation.

Ruby arrays are not lazy, use enumarators when necessary.

In Ruby 2.0:

(0..Float::INFINITY).lazy.map { |x| 2*x }.take(5).to_a # [0 2, 4, 6, 8]

Page 21: Ruby Functional Programming

Ruby Functional Programming

Recursion Looping by calling a function from within itself. When you don’t have access to mutable data, recursion is used to build up and chain data construction. This is because looping is not a functional concept, as it requires variables to be passed around to store the state of the loop at a given time.

● Purely functional languages have no imperative for-loops, so they use recursion a lot.

● If every recursion created an stack, it would blow up very soon.

● Tail-call optimization (TCO) avoids creating a new stack when the last call in a recursion is the function itself.

● TCO is optional in Ruby: you cannot rely on it in your code if you want to use it everywhere.

● Unfortunarely following recursion style in Ruby has it’s own tax: Performance.

Page 22: Ruby Functional Programming

Ruby Functional Programming

Recursion To enable TCO in MRI-1.9:

Simple factorial example:

RubyVM::InstructionSequence.compile_option = { :tailcall_optimization => true, :trace_instruction => false, }

def factorial_tco(n, acc=1) n < 1 ? acc : factorial_tco(n-1, n*acc) end

Page 23: Ruby Functional Programming

Ruby Functional Programming

FP in OOP? It is possible do FP in OOP? Yes it is!

● OOP is orthogonal to FP.

● Well, at least in theory, because:

○ Typical OOP tends to emphasize change of state in objects.

○ Typical OOP mixes the concepts of identity and state.

○ Mixture of data and code raises both conceptual and practical problems.

● OOP functional languages: Scala, F#, ...

Page 24: Ruby Functional Programming

Ruby Functional Programming

A Pratical Example Exercise: "What's the sum of the first 10 natural number whose square value is divisible by 5?"

Imperative:

Functional:

n, num_elements, sum = 1, 0, 0 while num_elements < 10 if n**2 % 5 == 0 sum += n num_elements += 1 end n += 1 end sum #275

Integer::natural.select { |x| x**2 % 5 == 0 }.take(10).inject(:+) #275

Page 25: Ruby Functional Programming

Ruby Functional Programming

The last advice Learn at least one functional language, it will open your mind to a new paradigm becoming you a better programmer.

Some Functional Languages:

● Haskell

● ML (Standard ML, Objective Caml, ...)

● Scheme

● Erlang

● Scala

● Closure

● F#

Page 26: Ruby Functional Programming

Ruby Functional Programming

Conclusion ● As you can tell, Ruby helps you write in functional style but it doesn’t force you to

it.

● Writing in functional style enhances your code and makes it more self documented. Actually it will make it more thread-safe also.

● The main support for FP in ruby comes from the use of blocks and lambdas, also from the fact that everything is evaluated as an expression.

● Ruby still lack an important aspect of FP: Pattern Matching and Lazy Evaluation.

● There should be more work on tail recursion optimization, to encourage developers to use recursion.

● Any other thoughts?

Page 28: Ruby Functional Programming

Ruby Functional Programming

Contact me ● Email:

[email protected]

● Skype

○ geisonfgf

● Facebook

○ http://www.facebook.com/geisonfgf

● Twitter

○ http://www.twitter.com/geisonfgf