update() {
    const deps = this.tree.project(this.projection);

    const lazyGetter = ((tree, def, data) => {
      let cache = null,
          alreadyComputed = false;

      return () => {

        if (!alreadyComputed) {
          cache = def.getter.apply(
            tree,
            def.type === 'object' ?
              [data] :
              data
          );

          if (tree.options.immutable && def.options.immutable !== false)
            deepFreeze(cache);

          // update tree affected paths
          const hash = hashPath(this.path);
          tree._affectedPathsIndex[hash] = true;

          alreadyComputed = true;
        }

        return cache;
      };
    })(this.tree, this.definition, deps);

    lazyGetter.isLazyGetter = true;

    // Should we write the lazy getter in the tree or solve it right now?
    if (this.tree.options.lazyMonkeys) {
      this.tree._data = update(
        this.tree._data,
        this.path,
        {
          type: 'monkey',
          value: lazyGetter
        },
        this.tree.options
      ).data;
    }
    else {
      const result = update(
        this.tree._data,
        this.path,
        {
          type: 'set',
          value: lazyGetter(),
          options: {
            mutableLeaf: !this.definition.options.immutable
          }
        },
        this.tree.options
      );

      if ('data' in result)
        this.tree._data = result.data;
    }

    // Notifying the monkey's update so we can handle recursivity
    this.tree.emit('_monkey', {monkey: this, path: this.path});

    return this;
  }
Method used to update the tree's internal data with a lazy getter holding
the computed data.

@return {Monkey} - Returns itself for chaining purposes.