Question on Immutability

H. S. Teoh hsteoh at quickfur.ath.cx
Tue Aug 31 00:46:24 UTC 2021


On Mon, Aug 30, 2021 at 11:27:07PM +0000, Merlin Diavova via Digitalmars-d-learn wrote:
> Hi All,
> 
> I'm trying to understand immutability in D and it seems a bit odd.
> I'm coming from dynamic languages so please forgive my ignorance and
> dynamic language-isms.
> 
> I want to have a base `Project interface` and then extend other more
> specific interfaces from that such as `DockerEnabledProject
> interface`, `NetworkEnabledProject interface` etc.
> 
> The interface implementations are immutable. I have defined some
> methods that allows one to change specific properties and return a new
> instance of the implementation.

> 
> ```d
> immutable interface Project
> {
>     string name();
>     immutable(Project) withName(string name); // Returns a new instance
> }
> 
> immutable class ShellScriptCLI : Project
> {
>     private string _name, _slug;
>     private DirectoryPath _directory;
> 
>     string name()
>     {
>         return this._name;
>     }
> 
>     immutable(Project) withName(string name)
>     {
>         return new immutable ShellScriptCLI(name, this._slug,
> this._directory);
>     }
> }
> 
> ...
> 
> auto project = new immutable ShellScriptCLI("Project One", "project-one",
> projectPath);
> auto modifiedProject = project.withName("G2 Project");
> assert(modifiedProject.name == "G2 Project");
> ```
> After playing around the above works, Great! However I have some questions
> 
> First, why do the interfaces have to be defined as `immutable interface`?
> The interfaces cannot be changed at runtime or instantiated.
>
> Secondly, why does defining the return type for withName as `Project`
> give the `Error: 'immutable' method 'winry.project.Project.name' is
> not callable using a mutable object`. However changing it to
> `immutable(Project)` works as expected.

You need to declare Project.name and Project.withName with either
`const` or `immutable`, like this:

     string name() const;
     immutable(Project) withName(string name) immutable;

The second `immutable` in .withName applies to the implicit `this`
parameter received by every method.  Without this qualifier you cannot
invoke it with an immutable object.

The interface itself does not need to be immutable; putting `immutable`
on it merely makes `immutable` the default attributes in member
declarations, which is likely not what you want if you will be defining
mutable data fields later on.  It happens to fix your compile error
because you forgot to put `const` or `immutable` on .name and .withName.

//

The best way to understand const/mutable/immutable in D is this little
diagram (forgive the ASCII art):

	       const
	      /     \
	mutable     immutable

Think of it as analogous to a class hierarchy diagram (a "type
hierarchy" if you will): both mutable and immutable implicit convert to
const, but const does not convert to either.

Basically, `const` means the holder of the reference is not allowed to
modify it.  So it doesn't matter whether the original data was mutable
or immutable: as far as the recipient is concerned, it cannot modify the
data, so everything is good.

`immutable` means that ALL references to the data cannot modify it (this
applies across threads too).  Const data *could* be modified by somebody
who happens to hold a mutable reference to it; immutable data cannot be
modified, period.  This means immutable can be freely shared across
threads (there are no mutable references to it, so the data never
changes and any thread can read it without needing to synchronize).

Furthermore, const/immutable in D is transitive, i.e., if a reference is
const, then any data it references is also implicitly const, and the
data referenced by said data, etc., is also const. Ditto with immutable.
We call it "turtles all the way down". :-)  In D you cannot have an
immutable reference to immutable data, and if you have a const reference
to something, you cannot modify anything it may refer to recursively.


T

-- 
The most powerful one-line C program: #include "/dev/tty" -- IOCCC


More information about the Digitalmars-d-learn mailing list