Jump to page: 1 2
Thread overview
January 11
So, I find that I increasingly miss head-const for immutable. And I understand that people don't want yet another way to modify the access patterns for a type, but maybe it is possible to avoid that, and do both.

So here is the idea:

Introduce "readonly" as head-immutable. That means that all values in an immutable array are immutable, including the address of reference, but not the objects being pointed to.

Then let "immutable" types be redefined to be a recursion of "readonly applications!

When you test a type for "immutable" the the checker would then not actually test for immutable, but test that the composite type is "readonly" at all levels.

Then we have two variants for the price of one!

What are the gotchas?

(You could probably do the same with const, but I think immutable has some potential performance benefits.)

January 11
On Monday, 11 January 2021 at 17:15:39 UTC, Ola Fosheim Grøstad wrote:
> So, I find that I increasingly miss head-const for immutable. And I understand that people don't want yet another way to modify the access patterns for a type, but maybe it is possible to avoid that, and do both.
>
> So here is the idea:
>
> Introduce "readonly" as head-immutable. That means that all values in an immutable array are immutable, including the address of reference, but not the objects being pointed to.

I tried implementing std.experimental.Final properly for structs. (For arrays, pointers and classes, Final could work well: The semantics of the operations of pointers and arrays are known and classes are reference types.) So, here are my thoughts.

Your readonly is near useless if you really want it to mean "head immutable" and not "head const". The value immutable provides is due to being transitive. "Head immutable" is unnecessary restrictive and has low guarantees.
For example, a head_immutable(int*)* cannot bind a int** variable, because immutable and mutable are referentially incompatible; head_const(int*)* can bind int** the same way const(int*)* can.

For that, I'd suggest `final` as the name. It's already a keyword and that's what it means on the first level. The difference to Java would be that it's not (Java's equivalent of) a storage class, but a type constructor. There would be final(int*)* which looks funny through Java eyes, but I think it could work.

One of the problems std.experimental.Final has: Say T is a struct (or union). On a Final!T object, you cannot call mutable or immutable methods, only const methods. But even const methods are too restrictive. If T has indirections, like a `int* ptr` member, mutating *ptrof a Final!T object would be fine. That's something a template cannot express.

So, final must also become a function attribute, too.

If you go with that DIP, it will probably be rejected because final would have to be implemented and maintained, while its application is very limited. There's probably a reason D went from D1 to D2 trading head const for transitive const.

final as a type constructor has only value on a middle part of indirection:
1. final(int**) can be treated as an int** except when referencing. The only gain is that you don't accidentally assign it.
2. final(int)** is the same as const(int)**.
3. Only final(int*)* is actually different from anything the vanilla language provides. What you'd have to argue is, how valuable is it? While final(T*) can easily be replaced by a struct template Ref!T (same goes for T[] and reference types), final(T) is interesting for structs T with indirections.
January 11
On Monday, 11 January 2021 at 19:05:13 UTC, Q. Schroll wrote:
> Your readonly is near useless if you really want it to mean "head immutable" and not "head const". The value immutable provides is due to being transitive. "Head immutable" is unnecessary restrictive and has low guarantees.
> For example, a head_immutable(int*)* cannot bind a int** variable, because immutable and mutable are referentially incompatible; head_const(int*)* can bind int** the same way const(int*)* can.

So, I disagree strongly with this. Head immutable is basically the default mutability of tuples in most languages. It is used a lot.

Const is too weak for the optimizer to make good use of. I basically never want transitive immutable. The only exception would be a lookup-table, but I have no problem modelling that manually...


> For that, I'd suggest `final` as the name. It's already a keyword and that's what it means on the first level.

No, final is semantically different. I understand what you mean, but mixing concepts like that is no good it makes a language more hard to read.

"readonly" is pretty standard with a very clear and concise meaning. E.g. if you put a pointer in read-only-memory then that means you cannot change the pointer, it does not mean that what is pointed to cannot be changed.

So "readonly" would be far more useful than immutable currently is, as it can modell anything immutable can.

This also means you can make much more immutable than today which may have optimization benefits.


> If you go with that DIP, it will probably be rejected because final would have to be implemented and maintained, while its application is very limited. There's probably a reason D went from D1 to D2 trading head const for transitive const.

Transitive const is a mistake. Transitive immutable is useful for lookup-structures, but that makes it very limited and also prevents extending such structures with caching mechanisms.

The fact that immutable cannot even model read-only-memory (ROM), which is very useful in system level programming, is a sign that it is inadequate.



January 13
On Monday, 11 January 2021 at 20:07:48 UTC, Ola Fosheim Grøstad wrote:
> On Monday, 11 January 2021 at 19:05:13 UTC, Q. Schroll wrote:
>> [...]
>
> So, I disagree strongly with this. Head immutable is basically the default mutability of tuples in most languages. It is used a lot.
>
> [...]

When I see readonly I think of this:
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly
January 13
On Wednesday, 13 January 2021 at 09:24:36 UTC, Imperatorn wrote:
> When I see readonly I think of this:
> https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/readonly

Yes, it is a common term, Typescript also uses it:

https://www.typescriptlang.org/docs/handbook/classes.html#readonly-modifier
https://www.typescriptlang.org/docs/handbook/utility-types.html

January 13
On Monday, 11 January 2021 at 17:15:39 UTC, Ola Fosheim Grøstad wrote:
> Introduce "readonly" as head-immutable. That means that all values in an immutable array are immutable, including the address of reference, but not the objects being pointed to.

This is an idiomatic break to the meaning of immutability which is global const, while readonly serves the purpose of being locally const.
You can mutate a readonly var outside the readonly scope, this is never possible with immutable, let it be either head, tail or head & tail immutable.


January 13
On Wednesday, 13 January 2021 at 15:14:29 UTC, sighoya wrote:
> On Monday, 11 January 2021 at 17:15:39 UTC, Ola Fosheim Grøstad wrote:
>> Introduce "readonly" as head-immutable. That means that all values in an immutable array are immutable, including the address of reference, but not the objects being pointed to.
>
> This is an idiomatic break to the meaning of immutability which is global const, while readonly serves the purpose of being locally const.
> You can mutate a readonly var outside the readonly scope, this is never possible with immutable, let it be either head, tail or head & tail immutable.

No, readonly would be one-level immutable. Basically the same as every other language. The only difference between readonly and D's immutable is that you can have immutable pointers to mutable data.

What you are talking of is head const?

January 13
On Wednesday, 13 January 2021 at 15:17:52 UTC, Ola Fosheim Grøstad wrote:
> No, readonly would be one-level immutable. Basically the same as every other language. The only difference between readonly and D's immutable is that you can have immutable pointers to mutable data.
>
> What you are talking of is head const?

Yep, head const is usually readonly or better final, readonly should be transitive.


I just find it confusing to denote readonly as head immutable because readonly in other languages denote head const, that's it.
January 13
On Wednesday, 13 January 2021 at 18:37:02 UTC, sighoya wrote:
> Yep, head const is usually readonly or better final, readonly should be transitive.

Why? "immutable" is transitive in D. readonly memory or registers are not transitive.


> I just find it confusing to denote readonly as head immutable because readonly in other languages denote head const, that's it.

No, readonly in technical specifications usually means that it is isn't possible to write to it at all. e.g. readonly hardware registers. They can still point to writable memory.

"readonly" in Typescript and C# is immutable, not const.

Which languages are you thinking of?

January 13
On Wednesday, 13 January 2021 at 19:22:07 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 13 January 2021 at 18:37:02 UTC, sighoya wrote:
>> Yep, head const is usually readonly or better final, readonly should be transitive.
>
> Why? "immutable" is transitive in D. readonly memory or registers are not transitive.
>
>
>> I just find it confusing to denote readonly as head immutable because readonly in other languages denote head const, that's it.
>
> No, readonly in technical specifications usually means that it is isn't possible to write to it at all. e.g. readonly hardware registers. They can still point to writable memory.
>
> "readonly" in Typescript and C# is immutable, not const.
>
> Which languages are you thinking of?

Is there even any value to having head-const in a language? As I think Walter has said before, it's basically just documentation/convention in C++. I can see the value in head-immutable, in terms of type system guarantees, but not head-const.
« First   ‹ Prev
1 2