Mutability

Most objects in Delta programs are immutable. This is several reasons.

Subtyping
Only trivial subtypes can be defined for a mutable type.

For example, ordinarily, Square is a subtype of Rectangle.

A mutable Rectangle would have setWidth w and setHeight h functions, with postconditions that the width and height functions will return the freshly set w and h value. The problem with that is that a Square with such an interface might lose its squareness property, or violate the postconditions of either setWidth or setHeight, or have to throw exceptions.

This may sound like a silly example, but a stretch function that modifies the elements of a list of geometric elements will work well on all Rectangles but fail on the first Square. In other words, a mutable Square can not be used in all contexts where a mutable Rectangle will work fine, so mutable Square is not a subtype of mutable Rectangle.

What works are subtypes that are trivial extensions. For example, adding a color attribute won't affect the contracts of setWidth or setHeight. (Equality can still cause confusion, but that is independent of mutability.) Of course, in such a situation, it's easier to create ColoredRectangle as a type that consists of a Rectangle and a Color.

The other thing that works are subtypes of incompletely specified interfaces. If a numeric type does not specify what will happen in case of a division with a remainder, subtypes can handle that case as they wish. However, doing division on such a number means you get potentially undefined behaviour. Still, this is a common pattern for event handlers, loop abstractions and other situations where some function is just passed through to act as it pleases, but...

Interaction with higher-order code
Delta is a functional language. This means that functions are often called far from the place where their name is written down, and where the exact order of calls is hidden behind several abstraction barriers.

Code that makes use of mutability critically depends on doing things in a specific order: the input for stage B is the output from stage A, so B must be called after A. This can become a serious problem if the functions A and B are passed around to different functions and get called in an unknown order.

Aliasing and optimization
Aliasing is if two names refer to the same object. Say, we have a function f p1 p2, and somebody calls 'f x x'. If f modifies a field in its p1 parameter, it will see the same modification "magically" appear in the corresponding field in p2.

This can also apply to subfields and sub-sub-fields of the objects involved, possibly several layers of abstraction down in the nitty-gritty details.

This effect is not only a cause for subtle bugs, it also severely limits the optimizations that the compiler can do: "Alias analysis" is one of the most important stages of any current-day compiler, and it is usually far too conservative to give really good results.

For immutable objects, aliasing is simply irrelevant, so it can assume that everything is unaliased and aggressively take any optimization opportunities.

Exceptions
If an exception occurs while mutating code is executing, the half-modified objects is in an inconsistent state. This means one of three things:
 * The object will cause further bugs later when some other computation depends on it;
 * the object needs to be discarded, losing information (and possibly making other objects inconsistent in the process, which then need to be discarded as well); or
 * the object needs to be rolled back to the state before the mutating operation started, which is an expensive operation.

In code that uses immutable objects, the exception will interrupt the construction of an object.

Parallel processing
Mutable objects need to be carefully protected from concurrent access using some (either expensive or woefully incomplete) synchronization mechanism.

From the perspective of the affected function, aliasing and concurrent access are the same problem, it will "see" unexpected changes in the objects it is using.

The fix would be the same in both cases: Place a write-prevention lock on every piece of data as soon as it is read or some property is used that depends on that data.

Having immutable data fixes both problems without the overhead.

Cheap copying
Copying an immutable object means you copy a pointer.

Copying a mutable object means you have to think about how deep the copy needs to be. Copy too much and you become inefficient, copy too little and you end up with changes affecting unexpected places (this is essentially the aliasing problem again). Oh, and if the copy is deep enough, be prepared for dealing with cycles.

When to use mutability anyway
The real world consists of mutable objects.

There are several applications where this does not mean you need mutability though.

One example is modelling real-world objects. You don't have physical objects in computer memory, you have abstractions of them. One possible abstraction would be a series of snapshots of the real-world object, each new snapshot built from the previous one; this is even efficient since we're in immutable land, so copying those parts of the snapshot that don't change means copying just a pointer.

The next area is interacting with mutable stuff around the program, such as screens, printers, files on disk, databases etc.

Again, you don't need mutable data objects to deal with these; instead of executing a change directly, the code can collect sequences of action descriptions. This allows for some nifty patterns (such as postprocessing an action sequence and optimizing it, or logging it, or letting the user decide which of them to actually execute), without losing much.

Of course, when all is said and done, the action lists need to be executed somewhere, and then you get to use the full glory of mutable data... but that's at the lowest levels of abstractions, and you can expect this to be wrapped in the bowels of a low-level library. You certainly don't need this for application programming.

The last area is optimization. Mutability does not go well with polymorphism ("Subtyping", above) or deeply nested data structures with potential aliasing, but there are situations where you need neither. Raw bitmap processing is one area where this can apply (though modern GPUs tend to change that), manipulating large matrices in numeric calculations may be another.

Still, optimization is the last thing applied to a program, and it has to be carefully weighed against the loss in flexibility. And sometimes the raw speed is worth the pain - just make sure you do get a real speed increase and the pain is tolerable.