Thread overview
Non-transitive immutable? Read only struct.
Dec 06, 2019
Gregor Mückl
Dec 06, 2019
Simen Kjærås
December 05, 2019
So, I get it, head-const has been discussed to death before. But it seems to me that it is more of a problem that there is no (to my awareness) non-transitive immutable. If there is, I apologize.

So, there are at least 4 situations where you really want that:

1. Protect fields in structs/classes after initialization. Reduces bugs. Documents intent.

2. Turn received values from functions into something that cannot be modified reliably irrespective of what the protection the called function has set for it. Reduces bugs.

3. To have sensible immutable tuples that can point to non-immutable things. In my view, a must have.

4. To have structures in ROM that can point to memory mapped registers or RAM. Not a must have, but sensible.

I'm not saying that immutable should change, but that there is need for something in addition to that. Call it "readonly" if you want.

December 06, 2019
On Thursday, 5 December 2019 at 17:01:07 UTC, Ola Fosheim Grøstad wrote:
> So, I get it, head-const has been discussed to death before. But it seems to me that it is more of a problem that there is no (to my awareness) non-transitive immutable. If there is, I apologize.
>
> So, there are at least 4 situations where you really want that:
>
> 1. Protect fields in structs/classes after initialization. Reduces bugs. Documents intent.
>
> 2. Turn received values from functions into something that cannot be modified reliably irrespective of what the protection the called function has set for it. Reduces bugs.
>
> 3. To have sensible immutable tuples that can point to non-immutable things. In my view, a must have.
>
> 4. To have structures in ROM that can point to memory mapped registers or RAM. Not a must have, but sensible.
>
> I'm not saying that immutable should change, but that there is need for something in addition to that. Call it "readonly" if you want.

Can you clarify how this goes beyond the classic OO pattern of private member variables with public getters and no setters? If there was a class-private visibility, D could emulate everything except point 3 on your list, I think, albeit somewhat verbosely. So how is your thinking diverging from that?
December 06, 2019
On Thursday, 5 December 2019 at 17:01:07 UTC, Ola Fosheim Grøstad wrote:

> 1. Protect fields in structs/classes after initialization. Reduces bugs. Documents intent.

immutable fields can be initialized in constructors. Getters allow you to define an interface that doesn't allow modification by outside forces while still allowing the class/struct to modify the fields if it feels like it, allowing both non-reassignable fields and lazy initialization.


> 2. Turn received values from functions into something that cannot be modified reliably irrespective of what the protection the called function has set for it. Reduces bugs.

Const does this. If you know it's immutable call assumeUnique on it. If you want to limit modification in some ways while allowing it in other ways, that sounds like a library thing, not something the language should do for you.


> 3. To have sensible immutable tuples that can point to non-immutable things. In my view, a must have.

Getters can be used for this.


> 4. To have structures in ROM that can point to memory mapped registers or RAM. Not a must have, but sensible.

Getters can do this. You'll probably have to dip into some un-@safe code, but this seems like an exotic enough use case that we can live with that.

--
  Simen
December 06, 2019
On Friday, 6 December 2019 at 08:45:15 UTC, Gregor Mückl wrote:
> Can you clarify how this goes beyond the classic OO pattern of private member variables with public getters and no setters? If there was a class-private visibility, D could emulate everything except point 3 on your list, I think, albeit somewhat verbosely. So how is your thinking diverging from that?

You cannot easily emulate it since this is low-level typing where you can obtain the address of the memory and that memory should be read only within its lifetime.

The problem with "immutable" is that it requires that read only memory only can point to read only memory, which is too limiting.

(You can try to set up a big machinery of emulating pointers etc, but then you are basically implementing a new type system within the type system. Which probably will be too cumbersome and therefore not used.)

December 06, 2019
On Friday, 6 December 2019 at 09:11:11 UTC, Simen Kjærås wrote:
> On Thursday, 5 December 2019 at 17:01:07 UTC, Ola Fosheim Grøstad wrote:
>
>> 1. Protect fields in structs/classes after initialization. Reduces bugs. Documents intent.
>
> immutable fields can be initialized in constructors.

But immutable fields cannot contain pointers to non-immutable memory?


> Getters allow you to define an interface that doesn't allow modification by outside forces while still allowing the class/struct to modify the fields if it feels like it, allowing both non-reassignable fields and lazy initialization.

Making fields/attributes read only is not only for the external interface it is also documentation for internal use, to prevent accidental modification in the implementation, prevent inheritance issues, enables caching based on typing in generic programming.

>> 2. Turn received values from functions into something that cannot be modified reliably irrespective of what the protection the called function has set for it. Reduces bugs.
>
> Const does this. If you know it's immutable call assumeUnique on it. If you want to limit modification in some ways while allowing it in other ways, that sounds like a library thing, not something the language should do for you.

No, const does not do it. If you receive a mutable value it will be const, not immutable. If you use immutable it will fail if the value contains an immutable pointer. (I am not talking about returned references, but value copies)

So there is no easy way to write this kind of code that has increased robustness in evolving codebases.


>> 3. To have sensible immutable tuples that can point to non-immutable things. In my view, a must have.
>
> Getters can be used for this.

That does not make the memory immutable as seen from the compiler?


>> 4. To have structures in ROM that can point to memory mapped registers or RAM. Not a must have, but sensible.
>
> Getters can do this. You'll probably have to dip into some un-@safe code, but this seems like an exotic enough use case that we can live with that.

Casting away immutable sounds very dangerous. If that can be done then the optimizer cannot use immutable for anything?

Anyway, getters are not useful in low level programming. You need to be able to take the address of the memory. So what you are left with is to leave it mutable to the typesystem and never write to it in your code, so basically no help from the type system.


December 06, 2019
On Friday, 6 December 2019 at 09:26:40 UTC, Ola Fosheim Grøstad wrote:
>>> 4. To have structures in ROM that can point to memory mapped registers or RAM. Not a must have, but sensible.
>>
>> Getters can do this. You'll probably have to dip into some un-@safe code, but this seems like an exotic enough use case that we can live with that.
>
> Casting away immutable sounds very dangerous. If that can be done then the optimizer cannot use immutable for anything?
>
> Anyway, getters are not useful in low level programming. You need to be able to take the address of the memory. So what you are left with is to leave it mutable to the typesystem and never write to it in your code, so basically no help from the type system.

I guess you could use const for ROM, but that might affect caching and ROM can be slow.

December 06, 2019
I guess I should have stated more clearly to major optimization advantages of having more memory set as immutable:

1. It allows the compiler to assume that there is no aliasing.

2. It allows the compiler to retain loaded values (registers/stack) even accross invalidation/barriers.

3. It allows generic code to retain cached copies of values/pointers obtained through a chain of immutable. Including pointers to mutable memory.