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

Immutable Da

Breno Ferreira
June 08, 2017
33

Immutable Da

Breno Ferreira

June 08, 2017
Tweet

Transcript

  1. Functional Programming Mais que funções, é sobre controlar side-effects Usar

    Map/Filter/Reduce é legal. É legal usar underscore/lodash/etc.. e usar uma variedade de funções auxiliares. Mas FP é bem mais que isso. FP é uma mudança de mindset Em praticamente todas as linguagens funcionais, o mindset do desenvolvedor é orientado por funções puras (sem side-effects), ou seja: dada uma entrada, ela irá, via de regra, sempre retornar o mesmo valor. E isso leva ao uso de estruturas de dados imutáveis.
  2. Dados mutáveis são side-effects Não desejados na maioria das vezes

    function soma(a, b) { lancaMissilNuclear(); return a + b; } Dados mutáveis em muitos casos dificultam a compreensão do código, principalmente quando não são explícitos para o caller da função. Exemplo de side-effects nao explicito function soma(a, b) { lancaMissilNuclear(); return a + b; } Em tempo de execução, uma série de mutações no estado de um objeto também dificulta a compreensão do data-flow da aplicação.
  3. Mudanças de estado são naturais Poucas aplicações são read-only, não

    é mesmo? Se fosse tudo conteúdo estático, nao precisaria de JS. HTML/CSS, dá pro designer fazer e tá tudo ok.
  4. > let obj = {a: 1, b: 2}; { a:

    1, b: 2 } > const immutableObj = Object.freeze(obj); { a: 1, b: 2 } > immutableObj.a = 3 { a: 1, b: 2 } Object.freeze retorna um objetivo imutável. Mas será que ele é útil? Não é explícita a diferença entre um objeto Frozen e Não-Frozen.
  5. > let obj = {a: 1, b: 2}; { a:

    1, b: 2 } > const immutableObj = Object.freeze(obj); { a: 1, b: 2 } > changed = Object.assign({}, immutableObj, {a:3}) { a: 3, b: 2 } Usando Object.assign, estamos criando cópias de toda a estrutura do objeto. Usado com muita frequencia, iremos causar uso alto de memória, e uma pressão alta no Garbage Collector. Fora que essa API é horrível
  6. Persistent Data Structures Soluções já adotadas por praticamente todas as

    linguagens funcionais, como Clojure, Haskell, OCaml, etc..
  7. Permite mutações, não in-place, mas sempre retornando um novo valor

    > var Immutable = require('immutable'); > var map1 = Immutable.Map({a:1, b:2, c:3}); Map { "a": 1, "b": 2, "c": 3 } > var map2 = map1.set('b', 50); Map { "a": 1, "b": 50, "c": 3 } > map1.get(‘b'); 2 > map2.get(‘b'); 50
  8. Higher order functions > const list = Immutable.List([1,2,3,4,5,6,7,8,9,10]); List [1,

    2, 3, 4, . . ., 9] > const list2 = list.filter(x => x % 2 === 0) .map(x => x * 2); List [4, 8, 12, 16] > const sum = list2.reduce((acc, x) => acc + x); 40 > list List [1, 2, 3, 4, . . ., 9]
  9. De/Para JS Objects > var nested = Immutable.fromJS({a:{b:{c:[3,4,5]}}}); // Map

    { a: Map { b: Map { c: List [ 3, 4, 5 ] } } } > var deep = Immutable.Map({ a: 1, b: 2, c: Immutable.List.of(3, 4, 5) }); > deep.toObject(); { a: 1, b: 2, c: List [ 3, 4, 5 ] } > deep.toJS(); { a: 1, b: 2, c: [ 3, 4, 5 ] }
  10. Performance Usando estruturas de HashMap Tries e Vector Tries, se

    baseando no ótimo trabalho do Chris Okasaki (Purely Functional Data Structures), é possível ter uma performance muito boa utilizando Persistent Data Structures.
  11. Equality > var map1 = Immutable.fromJS({a:{b:{c:[3,4,5]}}}); // Map { a:

    Map { b: Map { c: List [ 3, 4, 5 ] } } } > var map2 = Immutable.fromJS({a:{b:{c:[3,4,5]}}}); // Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } } > Immutable.is(map1, map2) true > Immutable.is(map1, Immutable.Map({a:1, b:2}) false
  12. Batching Mutations If a tree falls in the woods, does

    it make a sound? If a pure function mutates some local data in order to produce an immutable return value, is that ok? — Rich Hickey, Clojure
  13. Batching Mutations > const list1 = Immutable.List.of(1,2,3); //list nesse caso

    é uma lista mutável //o retorno de withMutations é imutável > const list2 = list1.withMutations(function (list) { list.push(4).push(5).push(6); }); > assert(list1.count() === 3); > assert(list2.count() === 6); Aplicando uma mutação para criar um novo objeto imutável tem um overhead, que pode causar uma leve queda de performance caso sejam feitas muitas alterações frequentemente. Se você precisa aplicar uma série de mutações localmente antes de retornar, Immutable lhe dá a capacidade de criar uma cópia temporária mutável (transitório) de uma coleção e aplicar um lote de mutações de uma forma performática usando withMutations. Na verdade, isso é exatamente como o próprio Immutable aplica mutações complexas.
  14. Immutable Single Store Redux abraça a ideia de manter o

    estado da aplicação explícito e imutável, usando funções puras (sem side-effects lembra?) sempre que possível. E todo o estado da aplicação existe em um único Atom: um objeto com valores aninhados contendo todos os dados necessários para a aplicação.
  15. Rollback de alterações é fácil Como as estruturas de dados

    são persistentes, e as versões antigas dos dados são mantidas, é bem fácil desfazer alguma operação. Basta setar o estado global da aplicação para seu valor anterior.
  16. Pure Components shouldComponentUpdate: function(nextProps) { return !Immutable.is(nextProps.value, this.props.value ); }

    Comparar se dois valores imutáveis são iguais é muito fácil, e isso facilita bastante na hora de ter componentes puros em React. Componentes puros em React são aqueles que, dado os mesmos props e state, não precisam ser re-renderizados. Isso pode dar um grande aumento de desempenho
  17. JS Objects Interop Immutable.Map não dá para usar map.property, e

    sim map.get(‘property’) Usar Immutable.Record ajuda, pois ele dá suporte a value.property, e é imutável. List não dá para usar list[0], e sim list.get(0) Libs de terceiros não são compativeis com Immutable values. `toJS()` nessa hora ajuda bastante. As vezes pode ser dificil saber se está trabalhando com um JS object, ou um Immutable object. Comentários ajudam. Usar um type-system (Typescript ou Flow) também podem ajudar bastante.