Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Fun with Ruby: Uma Abordagem Funcional
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Salviano Ludgério
September 19, 2015
Programming
63
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Fun with Ruby: Uma Abordagem Funcional
Palestra apresentada na RubyConf Brasil 2015
Salviano Ludgério
September 19, 2015
Other Decks in Programming
See All in Programming
SalesForceを内製化!? ~ HR事業を支える顧客管理基盤のインフラを大公開 ~
oku053
0
180
パスワード再設定メールのURLについて知っておこう
uyuki234
0
160
ActionController meets Protocol Buffers
sorah
0
520
ChromeでローカルLLM
ryu1013
0
140
すこし踏み込む CancellationToken
htkym
2
1.7k
作って学ぶSpring BootのAuto Configuration / JJUG Night Seminar 2026-10-19
rshindo
0
140
複雑なUIコンポーネントとの ⻑い付き合い⽅:波及するバグからテスト戦略を経て、安全な再設計へ
mixplace
0
410
Atomic Design, Enforced: Scaling Mobile Design Systems (next.app devCon / droidCon Berlin 2026)
steliosf
PRO
0
170
Long Living Tokens, Die, Die, Die! (or: The One Ring is Banned in Modern)
sylph01
0
300
Railsのためのパスキー実践入門 〜マルチテナントSaaSでの意思決定〜
utakaha
2
550
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
290
Kotlinの15年とServer-Side Kotlin
n_takehata
1
150
Featured
See All Featured
Reflections from 52 weeks, 52 projects
jeffersonlam
356
22k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
1
630
How STYLIGHT went responsive
nonsquared
100
6.3k
My Coaching Mixtape
mlcsv
0
360
The Spectacular Lies of Maps
axbom
PRO
1
1.1k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
710
Building AI with AI
inesmontani
PRO
1
1.3k
How to Ace a Technical Interview
jacobian
280
24k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.4k
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
600
Designing Powerful Visuals for Engaging Learning
tmiket
1
610
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
600
Transcript
Fun with Ruby Uma Abordagem Funcional
! ! ! ! ! ! ! ! ! !
! ! Salviano Ludgério
[email protected]
! @salvianoo
Agenda • Programação Funcional • Conceitos funcionais presentes no Ruby
• Características funcionais ausentes no Ruby • Exemplos
Disclaimer • Cálculo Lambda • Teoria das Categorias • Monads
Não sou expert
Programação Funcional
O que a Internet fala?
None
None
Functions as building blocks
Functions as data
Funções como principal mecanismo de abstração
Transformação de dados
Pipeline de funções que transformam dados
Funcional != OO
[1,2,3].map(n => n * 2)! ! map (n => n
* 2), [1,2,3]
O Renascimento das Linguagens Funcionais
Raízes na academia
Pouca presença na indústria
None
None
None
None
e também no Browser… ClojureScript Scala.js Elm LiveScript PureScript
Por que tanto entusiasmo?
Estruturas de Dados Imutáveis Concorrência
Legal! E o Ruby??
λ Lambdas! λ
No Cálculo Lambda λx.x
Scheme (define f! (lambda (x) x))
JavaScript var f = function(x) {! return x;! };
Ruby f = lambda { |a| a }
Ruby f = proc { |a| a }
Ruby f = Proc.new { |a| a }
Ruby f = ->(a) { a }
Ruby f = ->(a) { a } stabby lambda Ruby
1.9+
Ruby f = lambda { |a| a }! f =
proc { |a| a }! f = Proc.new { |a| a }! f = ->(a) { a }
Como chamar a função? funcao[:x]! funcao.(:x)! funcao.call :x! ! Tem
mais formas?
Proc X Lambda
Ambos são objetos Proc > lambda { }! #<Proc:0x007f97b3181828@(irb):1 (lambda)>!
! > proc { }! #<Proc:0x007f97b31786d8@(irb):2>
Proc x Lambda • Lambdas verificam a aridade da função
(número de argumentos) • Procs não fazem a verificação da aridade
Tratam o return de forma diferente def lambda_test! lam =
lambda { return }! lam.call! puts "Hello world"! end! ! lambda_test #=> Hello world def proc_test! prc = proc { return }! prc.call! puts "Hello world"! end! ! proc_test #=>
Blocos
[1,2,3].each do |a|! puts a * 2! end! ! [1,2,3].each(lambda
{|a|! puts a * 2! })
# Syntax Sugar! ! # Herança de Smalltalk! ! (1
to: 5) collect: [ :i | 2 * i ]
Blocos • São funções anônimas • Não são objetos •
Fazem parte da sintaxe de um método • Só podem aparecer na lista de argumentos de um método
Passando funções
def exec_func(f)! f.call(2)! end! ! dobro = ->(a) { a
* 2 }! ! exec_func(dobro) #=> 4
dobro = -> { |a| a * 2 }! !
[1,2,3].each(&dobro)
Passando Funções Ruby • Operador &: Transforma uma lambda/proc em
um bloco ruby
Passando Funções Ruby • Métodos que recebem bloco, usar operador
& para passar um lambda
• Maneira certa de usar e passar funções como valores
em Ruby: com lambdas e .call() • Procs se assemelham a procedures, não funções Passando funções
Closures
“Um objeto é dado com função. Uma closure é uma
função com dados.” ! John D. Cook
nome_aluno = 'Salviano'! ! boletim = ->(nota) {! !!"#{nome_aluno} tirou
#{nota}"! }! ! boletim.call 7.5
Função! +! Ambiente nome_aluno = 'Salviano'! ! boletim = ->(nota)
{! !!"#{nome_aluno} tirou #{nota}"! }
disciplina = "Matemática"! ! resultado = ->() {! nome_aluno =
'Salviano'! ! boletim = ->(nota) {! "#{nome_aluno} tirou #{nota} em #{disciplina}"! }! !! !!boletim.call(7.5)! }.call
Escopo Léxico
nome_aluno = 'Salviano'! ! def boletim(nota)! "#{nome_aluno} tirou #{nota}"! end!
! boletim(7.5)
nome_aluno = 'Salviano'! ! def boletim(nota)! "#{nome_aluno} tirou #{nota}"! end!
! boletim(7.5) NameError: Undefined local variable or method nome_aluno
( sobre métodos
Métodos não possuem escopo léxico
Logo, métodos não são closures
Não podem ser passados diretamente para outras funções.
Mas podem ser convertidos para objetos do tipo Method
) voltando com closures
class Pessoa! attr_accessor :name! ! def initialize(name)! @name = name!
end! end
Usar somente funções
Pessoa = ->(name) {! setname = ->(new_name) { name =
new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
Pessoa = ->(name) {! setname = ->(new_name) { name =
new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
Pessoa = ->(name) {! setname = ->(new_name) { name =
new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
Pessoa = ->(name) {! setname = ->(new_name) { name =
new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
pessoa = Pessoa.('John')! ! pessoa.(:getname) #=> John! pessoa.(:setname).('Joao') #=> Joao
+ exemplo de closures
Stack = ->() {! _data = []! ! push =
->(thing) {! _data << thing! }! ! ->(attr) {! return push if attr == :push! return _data if attr == :data! }! }.call!
Stack = ->() {! _data = []! ! push =
->(thing) {! _data << thing! }! ! ->(attr) {! return push if attr == :push! return _data if attr == :data! }! }.call!
Stack = ->() {! _data = []! ! push =
->(thing) {! _data << thing! }! ! ->(attr) {! return push if attr == :push! return _data if attr == :data! }! }.call!
Stack = ->() {! _data = []! ! push =
->(thing) {! _data << thing! }! ! ->(attr) {! return push if attr == :push! return _data if attr == :data! }! }.call!
Stack = ->() {! _data = []! ! push =
->(thing) {! _data << thing! }! ! ->(attr) {! return push if attr == :push! return _data if attr == :data! }! }.call! Stack.(:push).(1)! Stack.(:push).(2)! Stack.(:push).(3)! ! Stack.(:data) #=> [1,2,3]
var Stack = () => {! var _data = []!
! return {! push: thing => _data.push(thing),! data: _data! }! }(); Stack.push(1)! Stack.push(2)! Stack.push(3)! ! Stack.data() //=> [1,2,3]! JS
Stack.push(1)! Stack.push(2)! Stack.push(3)! ! Stack.data() //=> [1,2,3]! JS var Stack
= () => {! var _data = []! ! return {! push: thing => _data.push(thing),! data: _data! }! }();
• Escopo Léxico • Função + Ambiente, acesso as variáveis
no momento de sua definição • Muito usado em JS para criar Factory Functions Closures
Função de Alta Ordem
Uma função que recebe outras funções como argumento ou retorna
uma função como resultado.
Exemplo de Função de Alta Ordem
Pessoa = ->(name) {! setname = ->(new_name) { name =
new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
Currying
Processo de transformar uma função que recebe múltiplos argumentos em
uma função que recebe apenas um argumento e retorna outra função se algum argumento ainda estiver faltando.
Currying soma = ->(a) {! ->(b) { a + b
}! }! soma.(1).(2)
Proc#curry soma = ->(a, b) { a + b }!
soma_com_curry = soma.curry! ! soma_com_curry.(1, 2)! soma_com_curry.(1).(2)
Composição de Funções
Composição de Função f o g
Composição de Função • Funções são encadeadas de modo que
o output da primeira função é o input da segunda função. • Cria uma função que é o equivalente a f ( g ( x ) )
Composição de Função • pipeline operator do Elixir |> •
pipe operator do Unix |
def compose(f, g)! ->(a) { f.(g.(a)) }! end
class Proc! def compose(g)! ->(*args) { self.(g.(*args)) }! end! !
alias * :compose! end
Composição de Função soma = ->(a, b) { a +
b }! dobro = ->(a) { a * 2 }! ! soma_e_dobro = dobro * soma
⚠️⚠️⚠️⚠️
F = ->() {! map = ->(func, list) { list.map(&func)
}! filter = ->(func, list) { list.select(&func) }! ! ->(name) {! return map if name == :map! return filter if name == :filter! }! }.call
F.(:filter).call func, [1,2,3]
func = F.(:filter).call ->(a){ a }! ! func.call [1,2,3]
F = ->() {! map = ->(f, list) { list.map(&f)
}.curry! filter = ->(f, list) { list.select(&f) }.curry! ! ->(name) {! return map if name == :map! return filter if name == :filter! }! }.call
Descrição do Problema • Selecionar a posição e título dos
filmes da década de 90 com avaliação superior a 8.0
Movies = [! {! rank: 1,! title: 'The Shawshank Redemption',!
year_release: 1994,! rating: 9.2! },! {! rank: 2,! title: 'The Godfather',! year_release: 1972,! rating: 9.2! },! ]
nineties = F.(:filter).call ->(movie) do! year_release = movie[:year_release]! year_release >=
1990 && year_release <= 1999! end
rating_8 = F.(:filter).call ->(movie) {! movie[:rating] > 8.0! }! !
titles = F.(:map).call ->(movie) {! "#{movie[:rank]} - #{movie[:title]}"! }
process_movies = titles * rating_8 * nineties! process_movies.call(Movies)! ! #=>
["1 - Movie1", "2 - Movie2"]
Composição de Funções • Muito usado em linguagens funcionais
Collection Pipeline
Collection Pipeline Movies! !.select { |m| m[:year_release] >= 1990 }!
!.select { |m| m[:rating] > 8.0 }! !.map { |m| "#{m[:rank]} - #{m[:title]}" }
Recursão
Recursão • Comum em linguagens funcionais para processar listas, e
resolução de problemas em geral. • Favorece a imutabilidade • Ruby não possui mecanismo de tail call optimization, o que inviabiliza
Recursão def somatorio(func, a, b)! return 0 if a >
b! func.(a) + somatorio(func, a+1, b)! end! ! somatorio(double, 1, 5)
Lazy Evaluation
Enumerable#lazy (1..Float::INFINITY)! .map { |x| x * x }! .first(10)
Enumerable#lazy (1..Float::INFINITY)! .lazy! .map { |x| x * x }!
.first(10)
Conclusões
Conclusões • Os conceitos são universais, sendo aproveitados em várias
linguagens, como JavaScript • Aprender um novo paradigma de programação pode trazer mais aprendizados que aprender uma nova linguagem
Conclusões • Unhappy Lambda • Lack of tail call optimization
• Lack of immutable data structures • Compor funções não é trivial
e agora?
SICP
Obrigado! Perguntas?