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