4

I am currently working on a collection implementation in JavaScript. I need a mutable and an immutable version of that. So what I thought of first was something like:

  • Collection
  • MutableCollection extends Collection
  • ImmutableCollection extends Collection.

My main issue with this is that if I want to implement more specific collections like List or Set, they could still inherit from Collection while their Mutable/Immutable implementations could not inherit from MutableCollection / ImmutableCollection.

So this made me think whether having all three, Collection, MutableCollection and ImmutableCollection would actually bring any significant benefit.

An alternative approach which came to me would be to only have Collection and then have a Collection.prototype.makeImmutable or freeze there. I would then also introduce a Collection.requireMutableInstance( collectionInstance ) which can be used in functions explicitly requiring a mutable collection.

Here are my main concerns about putting everything into just Collection:

  • The prototype definition is getting much bigger compared to defineing a MutableCollection prototype as well which would then hold the code of all the operations for alteration. I guess I could solve this by just defining a second file anyhow which would then just extend the basic Collection definition with those additional operations and the makeImmutable operation.
  • Documentation of those function signatures that actually care about mutable vs. immutable and not just require the basic collection would look less pretty.

Take:

/**
 * @param {MutableCollection} collection
/*
function messAroundWithSomeCollection( collection ) { /* ... */ }

vs.

/**
 * @param {Collection} collection A collection not yet made immutable.
/*
function messAroundWithSomeCollection( collection ) { /* ... */ }

What would be the more natural way of doing this in JavaScript? Especially without having interfaces I can check against and considering the flatter chains of inheritance if I would also implement a Set and Map collection, the everything-in-one approach seems more intuitive to me.

4
  • 1
    Interesting question. I would contradict one statement you made though: ...in most cases, code should not really care about whether a given collection is mutable or not. : while ideally true, the semantics of dealing with immutable vs. mutable objects/collections can vary greatly, so I don't suspect you could get away with that assertion. Jul 15, 2013 at 19:50
  • I guess my statement is only true when reading the collection which I assumed would be the more common case. I will change the statement a little, thanks. Jul 16, 2013 at 13:19
  • If you don't want to simply recapitulate the existing Java util classes, you should take a look at the C++ STL, which neatly separates collections from operations. Also look at Google Guava, which fills many holes in the current collections classes. Sep 14, 2013 at 16:02
  • I believe a more standard approach is to have an immutable class with a constructor that takes the mutable form, makes the copy of it. Changing the mutability of a class on the fly feels kind of 'icky'.
    – user40980
    Nov 13, 2013 at 18:03

1 Answer 1

1

The "class hierarchy" that you mention depends a lot on what you put in Collection. For an immutable list what you usually need is a way of iterating through values: length, forEach, map, find, etc.

So what has the ImmutableCollection interface that is not already part of Collection? Another possible hierarchy could be:

  • Iterable: length, forEach
  • MutableCollection: implements Iterable, but adds mutators add, remove

About how to convert a mutable collection into immutable, use a decorator:

    new ImmutableView(mutableCollection)

Where ImmutableView implements Iterable but doesn't expose any mutator. That's the approach used by most collection APIs: Java, Scala, Dart. (Actually Java implementation is broken in the sense that Collection already have mutators, so an Immutable collection is as collection that doesn't follow the substitution principle: add/remove throws exceptions so semantically doesn't implement the interface)

About the Collection.requireMutable. Why do you want to do that? JavaScript is not statically typed, so what you want to do is to emulate some sort of type checking. If that really bothers you, switch to a language like TypeScript or Dart. If not, assume that you receive a mutable collection and don't do any additional check. Everything will be fine as long you:

  • Do unit tests. TDD is the best companion for big systems based on dynamic languages
  • Follow the Liskov substitution principle. Even if JavaScript doesn't have static typechecking, is good exercise to think about it. A quick rule of thumb, if your code has some if (x instanceof Y) conditionals.. you are not following the substitution principle. It means that you are not taking advantage of polymorphism.
2
  • A ReadableFoo, a ReadOnlyFoo, and an ImmutableFoo should make different promises; all should promise that items may be read out; the second should additionally promise that it cannot be typecast or otherwise manipulated to yield a reference which could be used to mutate the collection; the third should promise that no reference that could manipulate the collection could possibly exist. All three promises have independent uses, and would have value as separate types. As an alternative to using instanceof tests, one could have virtual methods AsMutable, AsReadOnly, and AsImmutable,...
    – supercat
    Jan 18, 2014 at 18:10
  • ...which would return either this, a wrapper, or a new object, as appropriate. It may also be helpful to have AsNewImmutable, although that wouldn't need to be virtual (since all types should implement it as return new MutableFoo(this);).
    – supercat
    Jan 18, 2014 at 18:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.