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

Fun with Ruby: Uma Abordagem Funcional

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →

Fun with Ruby: Uma Abordagem Funcional

Palestra apresentada na RubyConf Brasil 2015

Avatar for Salviano Ludgério

Salviano Ludgério

September 19, 2015

Other Decks in Programming

Transcript

  1. ! ! ! ! ! ! ! ! ! !

    ! ! Salviano Ludgério [email protected]! @salvianoo
  2. Agenda • Programação Funcional • Conceitos funcionais presentes no Ruby

    • Características funcionais ausentes no Ruby • Exemplos
  3. Ruby f = lambda { |a| a }! f =

    proc { |a| a }! f = Proc.new { |a| a }! f = ->(a) { a }
  4. Proc x Lambda • Lambdas verificam a aridade da função

    (número de argumentos) • Procs não fazem a verificação da aridade
  5. 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 #=>
  6. # Syntax Sugar! ! # Herança de Smalltalk! ! (1

    to: 5) collect: [ :i | 2 * i ]
  7. 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
  8. dobro = -> { |a| a * 2 }! !

    [1,2,3].each(&dobro)
  9. • 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
  10. “Um objeto é dado com função. Uma closure é uma

    função com dados.” ! John D. Cook
  11. disciplina = "Matemática"! ! resultado = ->() {! nome_aluno =

    'Salviano'! ! boletim = ->(nota) {! "#{nome_aluno} tirou #{nota} em #{disciplina}"! }! !! !!boletim.call(7.5)! }.call
  12. nome_aluno = 'Salviano'! ! def boletim(nota)! "#{nome_aluno} tirou #{nota}"! end!

    ! boletim(7.5) NameError: Undefined local variable or method nome_aluno
  13. Pessoa = ->(name) {! setname = ->(new_name) { name =

    new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
  14. Pessoa = ->(name) {! setname = ->(new_name) { name =

    new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
  15. Pessoa = ->(name) {! setname = ->(new_name) { name =

    new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
  16. Pessoa = ->(name) {! setname = ->(new_name) { name =

    new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
  17. Stack = ->() {! _data = []! ! push =

    ->(thing) {! _data << thing! }! ! ->(attr) {! return push if attr == :push! return _data if attr == :data! }! }.call!
  18. Stack = ->() {! _data = []! ! push =

    ->(thing) {! _data << thing! }! ! ->(attr) {! return push if attr == :push! return _data if attr == :data! }! }.call!
  19. Stack = ->() {! _data = []! ! push =

    ->(thing) {! _data << thing! }! ! ->(attr) {! return push if attr == :push! return _data if attr == :data! }! }.call!
  20. Stack = ->() {! _data = []! ! push =

    ->(thing) {! _data << thing! }! ! ->(attr) {! return push if attr == :push! return _data if attr == :data! }! }.call!
  21. 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]
  22. 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
  23. 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! }! }();
  24. • 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
  25. Pessoa = ->(name) {! setname = ->(new_name) { name =

    new_name }! ! ->(attr) {! return name if attr == :getname! return setname if attr == :setname! }! }
  26. 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.
  27. Proc#curry soma = ->(a, b) { a + b }!

    soma_com_curry = soma.curry! ! soma_com_curry.(1, 2)! soma_com_curry.(1).(2)
  28. 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 ) )
  29. Composição de Função soma = ->(a, b) { a +

    b }! dobro = ->(a) { a * 2 }! ! soma_e_dobro = dobro * soma
  30. 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
  31. 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
  32. 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
  33. Movies = [! {! rank: 1,! title: 'The Shawshank Redemption',!

    year_release: 1994,! rating: 9.2! },! {! rank: 2,! title: 'The Godfather',! year_release: 1972,! rating: 9.2! },! ]
  34. rating_8 = F.(:filter).call ->(movie) {! movie[:rating] > 8.0! }! !

    titles = F.(:map).call ->(movie) {! "#{movie[:rank]} - #{movie[:title]}"! }
  35. Collection Pipeline Movies! !.select { |m| m[:year_release] >= 1990 }!

    !.select { |m| m[:rating] > 8.0 }! !.map { |m| "#{m[:rank]} - #{m[:title]}" }
  36. 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
  37. Recursão def somatorio(func, a, b)! return 0 if a >

    b! func.(a) + somatorio(func, a+1, b)! end! ! somatorio(double, 1, 5)
  38. 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
  39. Conclusões • Unhappy Lambda • Lack of tail call optimization

    • Lack of immutable data structures • Compor funções não é trivial