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

Programação Funcional em JavaScript na prática com Ramda

Programação Funcional em JavaScript na prática com Ramda

Não existe solução única para todos os problemas em tecnologia. O mesmo acontece para linguagens de programação! Aprenderemos mais sobre elas através do paradigma de programação funcional. Abordaremos paradigmas de programação, programação funcional e conceitos relacionados como funções de primeira classe, alta ordem e puras, estados, imutabilidade, transparência referencial, funções parciais, currying, composição e recursão. Veremos exemplos utilizando JavaScript puro e a biblioteca Ramda.js construída para o estilo de programação funcional.

52711e2157a6fed933b0361cc06a6953?s=128

Marcel dos Santos

July 21, 2018
Tweet

Transcript

  1. Marcel Gonçalves dos Santos @marcelgsantos programação funcional na prática com

    Ramda JavaScript em
  2. pensandonaweb.com.br desenvolvedor web full-stack Marcel Gonçalves dos Santos @marcelgsantos

  3. None
  4. @femugsp sp.femug.com #TheDevConf #TrilhaJavaScript

  5. @phpsp phpsp.org.br

  6. O que é programação funcional?

  7. paradigma de programação que utiliza funções puras e foca na

    transformação do estado
  8. O que é paradigma de programação?

  9. são modelos ou estilos de programação suportados por linguagens que

    agrupam certas características comuns
  10. os paradigmas de programação definem como os códigos são estruturados…

  11. principais paradigmas de programação
 os dois principais paradigmas são o

    imperativo e o declarativo
  12. paradigma imperativo
 descreve a resolução de um problema através de

    comandos que o computador pode compreender e executar
  13. paradigma imperativo
 os paradigmas procedural e orientado a objetos são

    exemplos de paradigmas imperativos
  14. let result = 0; !// Imperative code to sum 1

    to 10 for (let i = 0; i !<= 10; i!++) { result += i; } console.log(result); !// 55
  15. paradigma declarativo
 permite especificar o que deve ser computado e

    não como deve ser computado
  16. paradigma declarativo
 os paradigmas funcional e lógico são exemplos de

    paradigmas declarativos
  17. const numbers = [1, 2, 3, 4, 5, 6, 7,

    8, 9, 10]; !// Declarative code to sum 1 to 10 const sum = (a, b) !=> a + b; const result = numbers.reduce(sum); console.log(result);
  18. paradigmas de algumas linguagens de programação
 
 imperativo
 procedural -

    C e Pascal orientado a objetos - C++, Java, PHP, Python e Ruby
 
 declarativo
 lógico - Prolog funcional - Clojure, Elixir, Elm, Erlang, F#, Haskell, Lisp, OCaml e Scala
  19. Programação Funcional λ

  20. paradigma de programação que utiliza funções puras e foca na

    transformação do estado
  21. baseado no cálculo lambda proposto por Alonzo Church na década

    de 30
  22. na programação funcional as funções são tratadas como conceito principal

  23. Uma função matemática trata-se de um simples mapeamento entre o

    domínio e o contra-domínio. 1 2 3 D B A C X Y
  24. JavaScript λ

  25. presente em todos os lugares como navegadores, servidores, mobile, desktop

    e dispositivos IoT
  26. muito popular e utilizada

  27. todos os programadores deveriam aprender

  28. possui suporte para diversas funcionalidades de programação funcional

  29. Estado λ

  30. o estado de um programa é representado pelos valores dos

    dados armazenados na memória…
  31. …em qualquer ponto de execução do programa

  32. o estado de uma aplicação é alterado a cada interação

    feita pelo usuário ou pelo próprio sistema…
  33. …e pode ser representado por uma estrutura de dados

  34. a maioria dos bugs são relacionados ao controle de estado

  35. Funções Puras λ

  36. 1. ter parâmetros de entrada 
 2. não depender do

    estado externo 3. retorno baseado nos valores de entrada 
 4. não devem causar efeitos colaterais funções puras
  37. !// pure or impure function? let counter = 0; function

    increment() { counter!++; return counter; } console.log(increment()); !// 1
  38. !// pure function (ES5 syntax) function add(x, y) { return

    x + y; } console.log(add(2, 3)); !// 5
  39. !// pure function (ES6 syntax) const add = (x, y)

    !=> x + y; console.log(add(2, 3)); !// 5
  40. por que utilizar funções puras?
 são reutilizáveis, componíveis, fáceis de

    testar, fáceis de cachear e paralelizáveis
  41. transparência referencial
 propriedade que garante que a saída de uma

    função pura sempre será a mesma dado um mesmo conjunto de argumentos
  42. !// referential transparency const add = (x, y) !=> x

    + y; console.log(add(2, 3) !!=== 5); !// true console.log(5 !!=== 5); !// true
  43. pode não ser fácil criar funções puras

  44. porém, a restritividade ajuda a melhorar o foco

  45. Mais sobre Funções λ

  46. funções de alta ordem e 
 funções de primeira classe


    são funções que podem ser atribuídas a variáveis, passadas como argumentos e retornadas de uma função
  47. !// high-order function const add = (x, y) !=> x

    + y; const numbers = [1, 2, 3, 4, 5]; const sum = numbers.reduce(add); const sum10 = numbers.reduce(add, 10); console.log(sum); !// 15 console.log(sum10); !// 25
  48. funções anônimas
 funções que não possuem nome e que, geralmente,

    são passadas como argumento ou atribuídas
  49. !// anonymous function const numbers = [1, 2, 3, 4,

    5]; const sum = numbers.reduce((x, y) !=> x + y); console.log(sum); !// 15
  50. closures
 funções que possuem acesso à valores do escopo externo

  51. !// closure function function greet(greeting) { return function (name) {

    return `${greeting} ${name}!`; }; } const greet2 = greeting !=> name !=> `${greeting} ${name}!`; console.log(greet('Hello')('Mary')); !// Hello Mary! console.log(greet2('Hello')('John')); !// Hello John!
  52. recursão
 é quando uma função é definida em termos de

    si própria, ou seja, quando a função chama ela mesma
  53. diferença entre função e procedimento (procedure)
 uma função recebe um

    valor e retorna um resultado; um procedimento é um conjunto de comandos executados numa ordem
  54. “Don't think of functions as a collection of instructions. Think

    of them as non-destructive operations on input `double = n => n * 2;`” Eric Elliott, 2016. https:/ /twitter.com/_ericelliott/status/685172918784004097
  55. memoize
 técnica que permite que funções custosas sejam cacheadas para

    execuções posteriores mais rápidas
  56. Imutabilidade λ

  57. a imutabilidade diz que um dado não pode ser alterado

    após a sua criação
  58. a imutabilidade permite maior confiança e evita que erros ocorram

  59. o JavaScript não possui suporte a dados imutáveis de forma

    nativa
  60. porém, pode-se trabalhar com dados imutáveis em JavaScript utilizando algumas

    técnicas
  61. !// using `const` to prevent reassignment const name = 'Alice';

    name = 'Bob'; !// TypeError: Assignment to constant variable.
  62. !// using `const` doesn't prevent object !// attributes changes const

    person = { id: 1, name: 'John', }; console.log(person); !// { id: 1, name: 'John' } person.name = 'Bob'; console.log(person); !// { id: 1, name: 'Bob' }
  63. !// adding a new property in an immutable way !//

    approach 1 - copying the old properties const person = { id: 1, name: 'John', }; const updatedPerson = { id: person.id, name: person.name, age: 25, }; console.log(person, updatedPerson); !// { id: 1, name: 'John' } { id: 1, name: 'John', age: 25 }
  64. !// adding a new property in an immutable way !//

    approach 2 - using spread operator const person = { id: 1, name: 'John', }; const updatedPerson = { !!...person, age: 25, }; console.log(person, updatedPerson); !// { id: 1, name: 'John' } { id: 1, name: 'John', age: 25 }
  65. !// updating a property in an immutable way !// (using

    spread operator) const person = { id: 1, name: 'John', }; const updatedPerson = { !!...person, name: 'Bob', age: 25, }; console.log(person, updatedPerson); !// { id: 1, name: 'John' } { id: 1, name: 'Bob', age: 25 }
  66. !// remove a property in an immutable way !// (using

    destructuring assignment) const person = { id: 1, name: 'John', age: 25, }; const {id, !!...personWithoutId} = person; console.log(person, personWithoutId); !// { id: 1, name: 'John', age: 25 } { name: 'John', age: 25 }
  67. Currying e aplicação parcial λ

  68. o currying é a técnica que permite transformar uma função

    que recebe múltiplos argumentos em uma função que…
  69. …recebe apenas um argumento e que retorna uma função que

    aceita os argumentos restantes
  70. !// uncurried function const greet = (greeting, name) !=> `${greeting}

    ${name}`; const greetMorning = greet('Good Morning'); console.log(greetMorning('Alice')); !// TypeError: greetMorning is not a function
  71. !// “curried" function const greet = greeting !=> name !=>

    `${greeting} ${name}`; const greetMorning = greet('Good Morning'); console.log(greetMorning('Alice')); !// Good Morning Alice
 console.log(greet(‘Good Morning', 'Alice')); !// error!
  72. !// curried function using Ramda.js const greet = R.curry((greeting, name)

    !=> `${greeting} ${name}`); console.log(greet('Good Morning')('Alice')); !// Good Morning Alice
 console.log(greet('Good Morning', 'Alice')); !// Good Morning Alice
  73. a aplicação parcial é quando se executa uma função e

    passa apenas parte de seus argumentos
  74. !// using a helper function that allows to perform !//

    partial application in a regular not curried function function add(x, y) { return x + y; } const add3 = partial(add, [3]); console.log(add3(2)); !// 5
  75. a aplicação parcial permite fazer a especialização de uma função

    mais genérica
  76. !// specialization from a curried function !// using partial application

    const greet = R.curry((greeting, name) !=> `${greeting} ${name}`); const greetMorning = greet('Good Morning'); console.log(greetMorning('Alice')); !// Good Morning Alice
  77. currying e aplicação parcial são recursos muito utilizados em programação

    funcional
  78. na programação funcional deve-se levar em consideração a ordem dos

    parâmetros
  79. os parâmetros mais genéricos devem vir mais para o início

    e os parâmetros mais específicos devem vir mais para o final
  80. o JavaScript não possui suporte nativo para currying como nas

    linguagens puramente funcionais Elm ou Haskell
  81. Composição de Funções λ

  82. a composição é o processo de combinar uma ou mais

    funções para criar uma nova função
  83. !// creating a new function from others by compostion const

    sentence = 'estava à toa na vida o meu amor me chamou pra ver a banda passar cantando coisas de amor'; const wordCount = R.length(R.split(' ', sentence)); console.log(wordCount); !// 19
  84. é uma solução elegante e legível e ajuda a evitar

    a utilização do aninhamento de funções
  85. o Ramda possui uma função que permite criar uma nova

    função a partir da composição de funções
  86. !// create a function using composition const sentence = 'estava

    à toa na vida o meu amor me chamou pra ver a banda passar cantando coisas de amor'; const countWords = R.compose(R.length, R.split); console.log(countWords(' ', sentence)); !// 19
  87. Biblioteca
 Ramda λ

  88. uma biblioteca construída para o estilo de programação funcional que

    facilita a utilização de pipelines e dados imutáveis
  89. possui foco no estilo puramente funcional

  90. todas as funções do Ramda são auto-curried

  91. os argumentos das funções do Ramda são organizados de forma

    a facilitar a utilização de currying
  92. Caso de Uso 1 somar os preços dos produtos de

    um carrinho de compras
  93. !// shopping cart const cart = [ {id: 1, product:

    'iPhone', price: 499}, {id: 2, product: 'Kindle', price: 179}, {id: 3, product: 'Macbook Pro', price: 1199}, ]; !// get prices from shopping cart and sum them
 !// using intermediate values const cartPrices = R.map(item !=> item.price, cart); const cartSum = R.sum(cartPrices); console.log(cartSum); !// 1877 realiza o mapeamento da lista de produtos (objetos) para uma lista de preços (números) faz a somatória da lista de números e retorna o total Passo 1
  94. !// shopping cart const cart = [ {id: 1, product:

    'iPhone', price: 499}, {id: 2, product: 'Kindle', price: 179}, {id: 3, product: 'Macbook Pro', price: 1199}, ]; !// get prices from shopping cart and sum them
 !// using function composition const totalCart = R.compose( R.sum, R.map(item !=> item.price), ); console.log(totalCart(cart)); !// 1877 cria uma nova função a partir da composição de funções e elimina valores intermediários Passo 2 aplicação parcial da função map a composição é feita da direita para a esquerda
  95. !// shopping cart const cart = [ {id: 1, product:

    'iPhone', price: 499}, {id: 2, product: 'Kindle', price: 179}, {id: 3, product: 'Macbook Pro', price: 1199}, ]; !// get prices from shopping cart and sum them !// using function composition with pipe const totalCart = R.pipe( R.map(item !=> item.price), R.sum, ); console.log(totalCart(cart)); !// 1877 Passo 3 o pipe de funções é feito da esquerda para a direita e facilita a leitura do código
  96. Caso de Uso 2 limpar dados vindo de um formulário

    e realizar um cálculo
  97. !// cleaning data from an input const price = '100';

    const discount = (perc, value) !=> perc * value; let priceInt = parseInt(price); let priceDiscount = discount(0.2, priceInt); console.log(priceDiscount); !// 20 Passo 1
  98. !// cleaning data from an input const price = '100';

    const discount = (perc, value) !=> perc * value; !// using partial application const discount20 = R.partial(discount, [0.2]); let priceInt = parseInt(price); let priceDiscount = discount20(priceInt); console.log(priceDiscount); !// 20 Passo 2 cria uma nova função a partir da aplicação parcial de uma existente
  99. !// cleaning data from an input const price = '100';

    const discount = (perc, value) !=> perc * value; !// using function composition const priceDiscount = R.pipe( parseInt, R.partial(discount, [0.2]), ); console.log(priceDiscount(price)); !// 20 Passo 3 cria uma nova função a partir da composição de funções utilizando a função pipe e elimina valores intermediários
  100. !// cleaning data from an input const price = 'lambda!';

    const discount = (perc, value) !=> perc * value; !// using function composition const priceDiscount = R.pipe( parseInt, R.partial(discount, [0.2]), ); console.log(priceDiscount(price)); !// null Passo 4 erro ao receber um valor não numérico
  101. !// cleaning data from an input const price = 'lambda!';

    const discount = (perc, value) !=> perc * value; !// using function composition const priceDiscount = R.pipe( parseInt, R.defaultTo(0), R.partial(discount, [0.2]), ); console.log(priceDiscount(price)); !// 0 Passo 5 retorna o valor padrão para o caso de um valor não truthy
  102. Outras Bibliotecas λ

  103. 1. RxJS
 2. Lodash
 3. Immutable.js 
 4. HyperApp outras

    bibliotecas
  104. Outras Linguagens
 Funcionais λ

  105. recomenda-se experimentar alguma linguagem de programação puramente funcional

  106. utilizar uma linguagem puramente funcional permite restringir a utilização da

    linguagem…
  107. …e evita a utilização de código imperativo ou mutável

  108. sugestão
 utilize a linguagem de programação Elm

  109. porém, no front-end, pode-se utilizar também PureScript ou ClojureScript

  110. Conclusão

  111. a programação funcional não é sobre não ter estado…

  112. …e sim sobre eliminar estado e efeito colateral sempre que

    possível e controlar efeitos colaterais quando necessário
  113. existem inúmeros conceitos relacionados a programação funcional como functors, mônades,

    lazy evaluation, lens, tail call optimization…
  114. vá em frente e divirta-se!

  115. Referências

  116. 1. Mostly Adequate Guide
 2. Functional-Light JavaScript
 3. Ramda Docs

    
 4. JavaScript Funcional - Arthur Xavier referências
  117. Avalie!

  118. @marcelgsantos speakerdeck.com/marcelgsantos Obrigado. Perguntas?