In some cases, immutability forces you to clone the object and needs to allocate more memory. It doesn't necessary occupy the memory, because older copies can be discarded. For example, the CLR garbage collector deals with this situation quite well, so this isn't (usually) a big deal.
However, chaining of operations doesn't actually mean cloning the object. This is certainly the case for functional lists. When you use them in the typical way, you only need to allocate a memory cell for a single element (when appending elements to the front of the list).
Your example with image processing can be also implemented in a more efficient way. I'll use C# syntax to keep the code easy to understand without knowing any FP (but it would look better in a usual functional language). Instead of actually cloning the image, you could just store the operations that you want to do with the image. For example something like this:
class Image {
Bitmap source;
FileFormat format;
float newWidth, newHeight;
float rotation;
// Public constructor to load the image from a file
public Image(string sourceFile) {
this.source = Bitmap.FromFile(sourceFile);
this.newWidth = this.source.Width;
this.newHeight = this.source.Height;
}
// Private constructor used by the 'cloning' methods
private Image(Bitmap s, float w, float h, float r, FileFormat fmt) {
source = s; newWidth = w; newHeight = h;
rotation = r; format = fmt;
}
// Methods that can be used for creating modified clones of
// the 'Image' value using method chaining - these methods only
// store operations that we need to do later
public Image Rotate(float r) {
return new Image(source, newWidth, newHeight, rotation + r, format);
}
public Image Resize(float w, float h) {
return new Image(source, w, h, rotation, format);
}
public Image ConvertTo(FileFormat fmt) {
return new Image(source, newWidth, newHeight, rotation, fmt);
}
public void SaveFile(string f) {
// process all the operations here and save the image
}
}
The class doesn't actually create a clone of the entire bitmap each time you invoke a method. It only keeps track of what needs to be done later, when you'll finally try to save the image. In the following example, the underlying Bitmap
would be created only once:
var i = new Image("file.jpg");
i.Resize(500, 800).Rotate(90).ConvertTo(Gif).SaveFile("fileNew.gif");
In summary, the code looks like you're cloning the object and you're actually creating a new copy of the Image
class each time you call some operation. However, that doesn't mean that the operation is memory expensive - this can be hidden in the functional library, which can be implemented in all sorts of ways (but still preserv the important referential transparency).