Introdução à Programação Funcional (com Ruby!)

marcelocamargo@linuxmail.org

github.com/haskellcamargo

About.me

  • Systems analyst;
  • Translator (PT/EN/ES/FR);
  • PL researcher;
  • Developer of Quack and Capybara programming languages.

TOC

× What in the world is FP?

× FP matters!

× FP concepts in Ruby

Functional Programming is

fun _ → b

What in
the World
is FP?

Paradigma de programação antigo

... mas muito alinhado ao presente

Everything
is a
function

No início, tudo era muito lento...

Ao projetar uma linguagem...

Iniciar na arquitetura de von Neumann* e adicionar abstração...

... Iniciar com bases matemáticas e remover

abstração

* programas e dados contidos no mesmo espaço de memória

OU

Computadores não eram suficientemente poderosos para lidar com tamanha abstração...

LISP era lento e incapaz de atender às demandas da época

Então, dominou a programação imperativa...

MAS
ISSO
MUDOU!

E FP GANHOU
UMA SEGUNDA
CHANCE!

Mas qual a diferença?

Não especifique como deve ser feito.

Especifique o que deve ser feito!

Vamos mergulhar nos conceitos básicos!

Usando Ruby!

FP
concepts
in Ruby

Conceitos básicos

Funções de primeira classe

Funções de mais alta ordem

Closures

Funções puras e determinísticas

Funções de primeira classe

Transparência referencial

Conceitos básicos

Imutabilidade

Pattern matching

Recursividade

Composição de funções

Não se assuste com os nomes!

Funções de primeira classe

add = lambda { |a, b| a + b }

puts add[10, 20]

Métodos, procedimentos e lambdas armazenados em variáveis

Funções de mais alta ordem

(1 .. 10) 
  .map { |x| x * 3 }
  .select { |x| x % 2 == 0 }
  .reduce { |acc, x| acc + x }

Passe funções como parâmetros e retorne outras funções

Closures

def add(a, b)
  a + b
end

add_10 = method(:add).curry[10]
puts add_10[20]

Acesse e preserve o escopo de execução anterior

Closures

def add(a)
  lambda { |b| a + b }
end

add_10 = add 10
puts add_10[20]

Acesse e preserve o escopo de execução anterior

Funções puras e determinísticas

def double_me(n)
  n[0] = n[0] * 2
end

val = [20]
double_me val
puts val[0]

f(x) = y. Sem efeitos colaterais. Se entra x agora e sai y, sempre sairá y quando entrar x. Exemplo impuro:

Funções puras e determinísticas

def double_me(n)
  n * 2
end

puts double_me 20

f(x) = y. Sem efeitos colaterais. Se entra x agora e sai y, sempre sairá y quando entrar x. Exemplo puro:

Transparência referencial

a = Math.sin 70
b = 80 - 30

puts a * b
puts (Math.sin 70) * (80 - 30)

Equivalência na substituição

Imutabilidade

example = "wow such example"
example.upcase!

puts example

"Variáveis" não variáveis! Devem servir como aliases.

ERRADO:

Imutabilidade

example = "wow such example"
result = example.upcase

puts result

"Variáveis" não variáveis! Devem servir como aliases. O estado deve trafegar, não ser alterado!

CORRETO:

Pattern matching

require 'pattern-match'
using PatternMatch

def fact(n)
  match (n) do
    with(0) { 1 }
    with(n) { n * fact(n - 1) }
  end
end

Defina o fluxo do programa através da forma dos seus valores

https://github.com/k-tsj/pattern-match

Recursividade

def fib(n)
  if (0..1).include? n
    then n
    else fib(n - 1) + fib(n - 2) if n > 1
  end
end

puts fib 20

Esqueça os loops. Esqueça estruturas imperativas

Composição de funções

class Proc
  def self.compose(f, g)
    lambda { |*args| f[g[*args]] }
  end
  
  def *(g)
    Proc.compose(self, g)
  end
end

Combine funções e obtenha novas funções.

Vamos implementar composição na linguagem...

Composição de funções

inc = lambda { |x| x + 1 }
double = lambda { |x| x * 2 }

double_inc = double * inc

# (20 + 1) * 2 = 42
puts double_inc[20]

Combine funções e obtenha novas funções.

Vamos utilizar nossa implementação!

map
filter
reduce

map

Aplique uma função a cada elemento da lista e retorne uma nova lista

my_list = 1 .. 10
my_list_by_three = my_list.map do |x|
  x * 3
end

filter/select

Aplique uma função a cada elemento da lista e retorne uma nova lista

my_list = 1 .. 10
my_even_list = my_list.select do |x|
  x % 2 == 0
end

reduce

Usando um acumulador e x, aplique uma função ao resultado desta com acc e x como parâmetros

my_list = 1 .. 10
my_sum = my_list.reduce do |acc, x|
  acc + x
end

((((((((((1 + 2) + 3) + 4) + 5) + 6) + 7) + 8) + 9) + 10

FP
matters

OOP cannot save us anymore

Pelo menos em relação à concorrência e ao paralelismo

1 core ≃ 1k cores

Safer & better code

Escreva código seguro, puro, limpo e melhor

Parallelism

Aproveite o processamento paralelo em múltiplos processadores e núcleos

Testable code

Escreva testes unitários com maior facilidade e garanta que cada parte da aplicação é consistente

Referências & Recomendações

How to Design Programs ─ Felleisen, Findler, Flatt, Krishnamurthi

Tnx!

Questions?

Introdução à Programação Funcional (com Ruby!)

By Marcelo Camargo

Introdução à Programação Funcional (com Ruby!)

  • 1,928