Immutability

for functional javascript

Immu...what?

Functionnal Programming pillars

  • Purity
  • Higher-Order Functions
  • Immutability


... and also curying, function composition, referential integrity...

Mutable vs. Immutable state

In FP, there is NO variables


        var heroName = 'Logan'
        heroName     = 'Groot' // 🚫 Unauthorized!
      

Say goodbye to for/while loop

Why Immutability is important?

Mutables variables give you headache


        class Hero {
          constructor () {
            this.color = 'beige'
            this.strengh = 1
          }
        
          hulkify () {
            this.color = 'green'
            this.strengh = 1000
          }
        }
      

        const bruceBanner = new Hero()
        // ... do something ...
        bruceBanner.goToRestaurant()
        // ... do something ...
        bruceBanner.hulkify() // bring mutation
        // ... do something ...
        bruceBanner.fight()
      

Unwanted mutations


        const today = moment()
        const nextYear = today.add(1, 'years')
      
        today === nextYear // true
      

Unwanted mutations


        function plusOneYear (aDate) {
          return aDate.add(1, 'years')
        }
      
        const today = moment()
        const nextYear = plusOneYear(today)
        day === nextYear // true
      

Make your loop great again


        var squadAlpha = []
        for (let i=0; i < heroes.length; i++) {
          if (!heroes[i].isEvil && heroes[i].family !== 'DC Comics') {
            squadAlpha.push(stringifyHero(heroes[i]))
          }
        }
      
        // vs.
        const squadAlphaStr = heroes
          .filter(hero => !hero.isEvil && hero.family !== 'DC Comics')
          .map(hero => stringifyHero(hero))
      

Note: .map(), .filter() and .reduce() return a new array

What plain JS already offer?


        var hero = {
          name: 'Daredevil',
          location: {
            city: 'New York',
            district: 'Kitchen Hell'
          }
        }
      

const


        const hero = {
          name: 'Daredevil',
          location: {
            city: 'New York',
            district: 'Kitchen Hell'
          }
        }
      

        hero = {
          name: 'The Punisher'
        } 
        // Uncaught TypeError: Assignment to constant variable
      

The end.

Question?

Oops ;)


        hero.name = 'The Punisher'
        console.log(hero) // Object {name: "The Punisher"}
      
That's not a reason not to use it!

(again) What plain JS already offer?


        const hero = {
          name: 'Daredevil',
          location: {
            city: 'New York',
            district: 'Kitchen Hell'
          }
        }
      

JSON.parse(JSON.stringify())


        const copy = JSON.parse(JSON.stringify(hero))
      

Non-stringifiable types


        const hero = {
          name: 'Daredevil',
          sayHello: () => 'hello',
          theUndefinedProperty: undefined,
          symbol: Symbol()
        }
        JSON.parse(JSON.stringify(hero)) // "{"name":"Daredevil"}"
      
In array, Non-stringifiable are replaced by null

Warning with JSON serialisation/deserialisation

Circular reference


        const daredevil = {
          friends: [daredevil]
        }

        // Uncaught TypeError: Converting circular structure to JSON
      

Object.seal()


        Object.seal(hero)
        
        hero.weapon = 'staff'
        console.log(hero.weapon) // undefined
      

        hero.location.people = 1000000000
        console.log(hero.location.people) // undefined
      

Object.freeze()


        Object.freeze(hero)
        
        hero.name = 'Jessica Jones'
        console.log(hero.name === 'Jessica Jones') // false
        
        hero.location.city = 'Dublin'
        console.log(hero.location.city === 'Dublin') // true
        

Object.assign()


        const copy = Object.assign({}, hero)
        
        copy.name = 'Jessica Jones'
        console.log(hero.name === 'Jessica Jones') // false
        
        copy.location.city = 'Dublin'
        console.log(hero.location.city === 'Dublin') // true
      

Object Spread Properties

Stage 3 - Candidate (Babel/TS only)


        const newHero = {
          ...hero,
          name: 'The Punisher'
        }
        console.log(hero.name === newHero.name) // false

        newHero.location.district = 'Central Park'
        console.log(hero.location === newHero.location) // true
      

Same problem everywhere

deep copy

Performances


      const heroes = [
        ⋮
        { name: 'Daredevil', isReady: false, /* ... others properties ... */ },
        { name: 'Jessica Jones', isReady: false,  /* ... others properties ... */ },
        { name: 'Hulk', isReady: false,   /* ... others properties ... */ },
        { name: 'Gandalf', isReady: true,   /* ... others properties ... */ },
        ⋮
        (100 000 heroes)
      ]
      

Immutable data structures

Structural sharing

Structural sharing

Quick poll:

Who's using REACT?

react virtual dom tree

Quick poll:

Who's using Vue+Vuex?

fail

Libraries

immutable.js logo

By Facebook

Usage


      import Immutable from "Immutable"
      
      const list         = Immutable.List()
      const map          = Immutable.Map()
      const stack        = Immutable.Stack()
      const record       = Immutable.Record()
      const seq          = Immutable.Seq()
      

      const immutableHero = Immutable.fromJS(hero)
      const plainJsHero   = immutableHero.toJS()
      

Example


        const Immutable = require('../js/immutable.min.js')

        const heroSchedule = Immutable.Map({
          Monday: "Training",
          Tuesday: 'Training',
          Wednesday: 'Going out',
          Thursday: 'Find something to do',
          Friday: 'Prepare for the battle',
          Saturday: 'Save the world',
          Sunday: 'Relax'
        })
        

        const mondayActivity    = heroSchedule.get("Monday")
        const scheduleWithPizza = heroSchedule.set("Friday", "Pizza")
        const hasFriday         = heroSchedule.has("Friday")
        

Example


      const Immutable = require('../js/immutable.min.js')
      
      const heroes = [
        { name: 'Batman',         family: 'DC Comics', isEvil: false },
        { name: 'Harley Quinn',   family: 'DC Comics', isEvil: true  },
        { name: 'Legolas',        family: 'Tolkien',   isEvil: false },
        { name: 'Gandalf',        family: 'Tolkien',   isEvil: false },
        { name: 'Saruman',        family: 'Tolkien',   isEvil: true  }
      ]

      const marvel = [
        { name: 'Wolverine',      family: 'Marvel',    isEvil: false },
        { name: 'Deadpool',       family: 'Marvel',    isEvil: false },
        { name: 'Magneto',        family: 'Marvel',    isEvil: true  },
        { name: 'Charles Xavier', family: 'Marvel',    isEvil: false },
      ]
      
      const newList = Immutable
        .List.of(...heroes)
        .push(...marvel)
        .insert(null, {name: "Groot", family: "Marvel", isEvil: false})
        .map(item => {
          return Immutable.Map(item).set('name', Immutable.Map(item).get('name').toUpperCase())
        })
        .sort()
        .toJS()
      

Performances: Immutables data structures vs native methods

engineforce/ImmutableAssign


Immutable (Object.assign)
  Verification: P-PPPP-PP-PPPPP-PP-PPPPPP-PPPPP-PPPP-PPPP-PPPP
  Object: read (x500000): 17 ms
  Object: write (x100000): 107 ms
  Object: deep read (x500000): 8 ms
  Object: deep write (x100000): 215 ms
  Object: very deep read (x500000): 49 ms
  Object: very deep write (x100000): 290 ms
  Object: merge (x100000): 137 ms
  Array: read (x500000): 9 ms
  Array: write (x100000): 542 ms
  Array: deep read (x500000): 10 ms
  Array: deep write (x100000): 519 ms
Total elapsed = 1903 ms = 93 ms (read) + 1810 ms (write).

Immutable (immutable.js)
  Verification: P-PPPP-PP-PPPPP-PP-PPPPPP-PPPPP-PPPP-PPPP-PPPP
  Object: read (x500000): 16 ms
  Object: write (x100000): 42 ms
  Object: deep read (x500000): 125 ms
  Object: deep write (x100000): 104 ms
  Object: very deep read (x500000): 215 ms
  Object: very deep write (x100000): 174 ms
  Object: merge (x100000): 520 ms
  Array: read (x500000): 20 ms
  Array: write (x100000): 109 ms
  Array: deep read (x500000): 103 ms
  Array: deep write (x100000): 182 ms
Total elapsed = 1610 ms = 479 ms (read) + 1131 ms (write).

Immutable (Object.assign) + deep freeze
  Verification: P-PPPP-PP-PPPPP-PP-PPPPPP-PPPPP-PPPP-PPPP-PPPP
  Object: read (x500000): 18 ms
  Object: write (x100000): 230 ms
  Object: deep read (x500000): 29 ms
  Object: deep write (x100000): 429 ms
  Object: very deep read (x500000): 45 ms
  Object: very deep write (x100000): 641 ms
  Object: merge (x100000): 249 ms
  Array: read (x500000): 13 ms
  Array: write (x100000): 14352 ms
  Array: deep read (x500000): 35 ms
  Array: deep write (x100000): 13571 ms
Total elapsed = 29612 ms = 140 ms (read) + 29472 ms (write).

// node v8.2.1 Intel(R) Core(TM) i5-5200U CPU @ 2.20GHz
      

See also: swannodette/mori

conclusion

  • embrace the power of immutability
  • don't need to be a functional programmer to use immutable data structures

Thank you.

Questions?

Backup slides

Time traveling

Example: git, CQRS...

eslint-plugin-immutable

Rules:

  • no-let, no-var
  • no-this
  • no-mutation

jhusain/eslint-plugin-immutable