frptools / collectable

High-performance immutable data structures for modern JavaScript and TypeScript applications. Functional interfaces, deep/composite operations API, mixed mutability API, TypeScript definitions, ES2015 module exports.
MIT License
273 stars 14 forks source link

Removing elements from a mutable red-black tree affects its immutable clones #47

Closed not-an-aardvark closed 7 years ago

not-an-aardvark commented 7 years ago

Using current master (https://github.com/frptools/collectable/commit/84d7bdb4475e823f97405da408514e6fbe6d5d0b):

const RBT = require('./packages/red-black-tree');

const mutableTree = RBT.emptyWithNumericKeys(true);

RBT.set(1, 'one', mutableTree);
RBT.set(2, 'two', mutableTree);
RBT.set(3,'three', mutableTree);
RBT.set(4, 'four', mutableTree);

const immutableClone = RBT.clone(mutableTree, false);

RBT.remove(1, mutableTree);

console.log([...RBT.keys(immutableClone)])

Expected output:

[1, 2, 3, 4]

Actual output:

[2]

I encountered this when I tried to use an immutable clone for stable iterators in https://github.com/frptools/collectable/issues/44#issuecomment-314350166. I think I'm using the new API correctly (freeze/thaw seem to have been removed), but let me know if I'm misunderstanding how it's supposed to work.

axefrog commented 7 years ago

Ah, sorry yeah the documentation doesn't exist yet so I admit there's a little trial and error you have to go through at the moment.

The gotcha in this case is that cloning from mutable to immutable is an unsafe operation. Safe cloning must always be performed from an immutable source. What you need to do is perform any mutations you care about, then commit (formerly "freeze") the changes. To perform further mutations, create a mutable clone thereof. The methods you want are modify() (and variants) and commit().

Take a look at the jsdoc comments for mutation.ts in frptools/core. The main points are:

As I said, take a look at mutation.ts and let me know if you have any questions.

not-an-aardvark commented 7 years ago

I see, thanks for clarifying. Closing this issue since it's not a bug.