digitalmars.D - RfC for language feature: rvalue struct
- FeepingCreature (79/79) Jan 25 2023 Before I take on the effort of writing up and submitting a DIP,
- Basile B. (7/11) Jan 25 2023 Sometimes I'm thinking about `rvalue` as a new storage class. The
- FeepingCreature (4/15) Jan 25 2023 Yeah you need to pass it as a pointer for methods and such, but I
- Paul Backus (7/13) Jan 25 2023 I agree that `immutable struct` is a mistake. It's a weird
- FeepingCreature (18/33) Jan 25 2023 I mean, I think this is the problem. The only thing about `rvalue
- Paul Backus (13/30) Jan 25 2023 I agree that if we could start with non-refrenceability as the
- TheZipCreator (12/16) Jan 25 2023 I mean, couldn't you just do this?
- FeepingCreature (9/19) Jan 25 2023 The problem is that then you can't do
- Salih Dincer (28/35) Jan 28 2023 No problem:
- FeepingCreature (8/46) Jan 28 2023 No, I mean
- Salih Dincer (44/51) Jan 29 2023 Sorry for writing an answer without doing the necessary unit
- FeepingCreature (12/65) Jan 29 2023 I think you're missing the concept a bit:
- Salih Dincer (28/36) Jan 29 2023 In order to use it as you say, the following opCall() should be
- FeepingCreature (20/24) Jan 29 2023 But what prevents `num1.field ~= 6`?
- Dukc (4/9) Jan 30 2023 Isn't this what private/`@system` (DIP1035) fields with
- FeepingCreature (15/25) Jan 30 2023 Yes, the behavior of "private fields with public getters" is
- Dukc (17/25) Jan 30 2023 This can be accomplished by making return value of the getter
- FeepingCreature (15/22) Jan 30 2023 That's fair. We're coming at D like it's a functional language at
- FeepingCreature (25/32) Jan 30 2023 I need to emphasize this point. For instance, say that a struct
- Dukc (10/21) Jan 30 2023 I see. The idea about immutability is good IMO, but your ways to
- FeepingCreature (16/19) Jan 30 2023 Yes, again, the idea is that rvalue allows *overwriting mutable
- FeepingCreature (10/15) Jan 30 2023 Just to give an example, `maxElement` is actually straight up
- Dukc (5/11) Jan 31 2023 In that case I'd probably go with a mutable pointer to an
- FeepingCreature (4/16) Jan 31 2023 Okay, now how do you do that without causing GC load? :) Keep in
- H. S. Teoh (12/29) Jan 31 2023 Why would a mutable pointer to an immutable struct cause GC load? Isn't
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/11) Jan 31 2023 Why can't you use regular encapsulation?
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/8) Jan 31 2023 Or maybe it isn't (shrug). Anyway, you might want to look at the
- FeepingCreature (10/18) Jan 31 2023 Yes, that's exactly what we want!
- FeepingCreature (33/44) Jan 31 2023 Maybe it can be done as a rewrite?
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/14) Feb 01 2023 […]
- FeepingCreature (6/22) Feb 01 2023 I mean, since it's a *reduction* in capability, it would even be
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (8/12) Feb 01 2023 Yes, if you can find a single location that you can hook into and
- Jack Applegame (3/4) Jan 31 2023 Please, stop adding new features.
- Tim (7/20) Feb 01 2023 Maybe escape analysis could be used as an alternative. If the
Before I take on the effort of writing up and submitting a DIP, let me solicit feedback and see if anyone can see a reason why this idea is dumb and doesn't work. tl;dr: `immutable struct` was a mistake: it's too weak. `rvalue struct` is what we really want. lvalue: an expression that has an address. rvalue: an expression that does not. The names come from `a = b`: an "lvalue" (left value) is a thing that can appear on the left of the equals sign, an "rvalue" (right value) may only appear on the right. Example: `int a` is an lvalue. `5` is an rvalue. You can write `a = 5` but not `5 = a`. This is a summary of an idea from my DConf talk https://www.youtube.com/watch?v=eGX_fxlig8I I'm writing it up because I'm noticing that the `immutable` bugs are neverending and the workarounds are an endless hole into which effort and nerves are thrown to no perceivable change. What is it? Take this struct ``` rvalue struct S { int a; int[] b; } ``` Two rules: 1. A `rvalue` struct has `immutable` fields. `typeof(s)` is `S`, but `typeof(s.b)` is `immutable int[]`. 2. Any symbol that would be an lvalue to *a field of `S`* is instead an rvalue. That's it. What is the effect of this? This works: ``` S foo(S s) { S value = S.init; value = s; *&value = s; return s; } ``` This does not: ``` S s; s.a = 5; &s.a ((ref int i) {})(s.a); ``` Note that you can overwrite `value` all you want. You can take the address of `value`. It's not immutable. But its fields are immutable. Isn't that a problem? No: because its fields *don't exist.* They're rvalues. You can't address them, you can't reference them. You can never observe a constness violation on them, because you can only observe them as rvalues. `s.a` is effectively an accessor. D libraries like to behave like they can declare variables and assign values to them. (Oh, to live in such innocence!) This is all over Phobos, Dub, etc. `immutable struct` frustrates this belief. Because you could always take the address of a field, which would be `immutable T*`, you could see the value changing when you overwrite the variable - a constness violation. `immutable` solves this by preventing you from modifying the memory of the field while the pointer lives. This largely doesn't work, because people don't test with `immutable struct` in the first place. If an `rvalue struct` is used, the naive code works as before, but the type gets the correctness benefits of immutable: you can only construct a new value through the constructor. Some details about Funkwerk, fresh off `wc -l`: In the modern part of our codebase, we have 648 domain structs, out of which 278 are `immutable struct`, and at least another 129 are good candidates to make immutable. In those 113kloc, we have **no** pointers to struct fields. None. `immutable struct` puts on the language, on library developers and on end users, immense difficulty and effort to protect a usecase that isn't useful.
Jan 25 2023
On Wednesday, 25 January 2023 at 16:23:51 UTC, FeepingCreature wrote:Before I take on the effort of writing up and submitting a DIP, let me solicit feedback and see if anyone can see a reason why this idea is dumb and doesn't work. [...]Sometimes I'm thinking about `rvalue` as a new storage class. The problem with that is that rvalues data would be _logical_ rvalues. Structures or static arrays cannot generally be _physical_ rvalues, that does not work, allocas are required. But I think it's pretty clear for you already, right ?
Jan 25 2023
On Wednesday, 25 January 2023 at 17:09:18 UTC, Basile B. wrote:On Wednesday, 25 January 2023 at 16:23:51 UTC, FeepingCreature wrote:Yeah you need to pass it as a pointer for methods and such, but I want to even go so far as to say that methods on an rvalue struct should see `this` as rvalue. (Except constructors, I guess?)Before I take on the effort of writing up and submitting a DIP, let me solicit feedback and see if anyone can see a reason why this idea is dumb and doesn't work. [...]Sometimes I'm thinking about `rvalue` as a new storage class. The problem with that is that rvalues data would be _logical_ rvalues. Structures or static arrays cannot generally be _physical_ rvalues, that does not work, allocas are required. But I think it's pretty clear for you already, right ?
Jan 25 2023
On Wednesday, 25 January 2023 at 16:23:51 UTC, FeepingCreature wrote:Before I take on the effort of writing up and submitting a DIP, let me solicit feedback and see if anyone can see a reason why this idea is dumb and doesn't work. tl;dr: `immutable struct` was a mistake: it's too weak. `rvalue struct` is what we really want. [...]I agree that `immutable struct` is a mistake. It's a weird special case in the language, and because of that most D code is not prepared to deal with it. I am not convinced that the solution to this is to introduce a new language feature that is even weirder and specialer.
Jan 25 2023
On Wednesday, 25 January 2023 at 17:28:10 UTC, Paul Backus wrote:On Wednesday, 25 January 2023 at 16:23:51 UTC, FeepingCreature wrote:I mean, I think this is the problem. The only thing about `rvalue struct` that is "weird" and "special" is that it isn't the default. D opts you in, without your consent or asking, to a huge amount of language power when it comes to manipulating values. If you declare a variable, you can assign it, reassign it, take its address, access fields, take the address of arbitrary subfields... That works fine in a language like C, that deliberately makes no guarantees about anything, and if we want to go that way we may as well remove `private` and go back to monke. But D2 tries to have its cake and eat it too, to let you make code safe and predictable, while *still* opting you into mutability and referenceability and thus actually making some tasks straight up impossible (without heavy casting). `rvalue` is not a unique and special snowflake, but an attempt to return one tiny part of the language to simplicity and sanity. (If it were up to me, every struct and every variable would be `rvalue` unless you explicitly declared it otherwise.)Before I take on the effort of writing up and submitting a DIP, let me solicit feedback and see if anyone can see a reason why this idea is dumb and doesn't work. tl;dr: `immutable struct` was a mistake: it's too weak. `rvalue struct` is what we really want. [...]I agree that `immutable struct` is a mistake. It's a weird special case in the language, and because of that most D code is not prepared to deal with it. I am not convinced that the solution to this is to introduce a new language feature that is even weirder and specialer.
Jan 25 2023
On Wednesday, 25 January 2023 at 17:47:34 UTC, FeepingCreature wrote:I mean, I think this is the problem. The only thing about `rvalue struct` that is "weird" and "special" is that it isn't the default. D opts you in, without your consent or asking, to a huge amount of language power when it comes to manipulating values. If you declare a variable, you can assign it, reassign it, take its address, access fields, take the address of arbitrary subfields... That works fine in a language like C, that deliberately makes no guarantees about anything, and if we want to go that way we may as well remove `private` and go back to monke. But D2 tries to have its cake and eat it too, to let you make code safe and predictable, while *still* opting you into mutability and referenceability and thus actually making some tasks straight up impossible (without heavy casting). `rvalue` is not a unique and special snowflake, but an attempt to return one tiny part of the language to simplicity and sanity. (If it were up to me, every struct and every variable would be `rvalue` unless you explicitly declared it otherwise.)I agree that if we could start with non-refrenceability as the default and make referenceability opt-in, that would be a cleaner design than D's referenceability-by-default. Unfortunately, that option is not on the table, at least not for D. For D, the options actually available to us are "implement `rvalue struct` as an opt-in special case" or "don't do that." I remain unconvinced that the former is better language design than the latter. (Another way to put this is that "returning one tiny part of the language to simplicity and sanity" is worse than leaving the whole language *consistently* "insane.")
Jan 25 2023
On Wednesday, 25 January 2023 at 16:23:51 UTC, FeepingCreature wrote:Before I take on the effort of writing up and submitting a DIP, let me solicit feedback and see if anyone can see a reason why this idea is dumb and doesn't work. [...]I mean, couldn't you just do this? ```d struct Foo { immutable: int bar; int[] baz; } ``` Unless I'm missing something, I don't really see why an entirely new language construct is required.
Jan 25 2023
On Wednesday, 25 January 2023 at 23:56:20 UTC, TheZipCreator wrote:I mean, couldn't you just do this? ```d struct Foo { immutable: int bar; int[] baz; } ``` Unless I'm missing something, I don't really see why an entirely new language construct is required.The problem is that then you can't do ``` Foo foo; foo = Foo(2, [3]); ``` And that may look easy to avoid, but there's a plethora of bugs where for instance Phobos does just that.
Jan 25 2023
On Thursday, 26 January 2023 at 03:39:14 UTC, FeepingCreature wrote:The problem is that then you can't do ``` Foo foo; foo = Foo(2, [3]); ``` And that may look easy to avoid, but there's a plethora of bugs where for instance Phobos does just that.No problem: ```d struct Foo(T) { import std.conv : to; import std.traits : ImmutableOf; this(R)(R bar, R[] baz) { this.bar = bar; this.baz = baz.to!(ImmutableOf!T[]); } immutable: T bar; T[] baz; } void main() { immutable arr = [9, 8, 7]; auto num1 = Foo!int(1, [2, 3, 4]); auto num2 = Foo!int(1, arr); string str = "bcd"; auto str1 = Foo!char('a', str.dup); auto str2 = Foo!char('a', str); } ``` SDB 79
Jan 28 2023
On Saturday, 28 January 2023 at 19:35:49 UTC, Salih Dincer wrote:On Thursday, 26 January 2023 at 03:39:14 UTC, FeepingCreature wrote:No, I mean ``` num1 = num2; onlineapp.d(22): Error: cannot modify struct instance `num1` of type `Foo!int` because it contains `const` or `immutable` members ``` ```The problem is that then you can't do ``` Foo foo; foo = Foo(2, [3]); ``` And that may look easy to avoid, but there's a plethora of bugs where for instance Phobos does just that.No problem: ```d struct Foo(T) { import std.conv : to; import std.traits : ImmutableOf; this(R)(R bar, R[] baz) { this.bar = bar; this.baz = baz.to!(ImmutableOf!T[]); } immutable: T bar; T[] baz; } void main() { immutable arr = [9, 8, 7]; auto num1 = Foo!int(1, [2, 3, 4]); auto num2 = Foo!int(1, arr); string str = "bcd"; auto str1 = Foo!char('a', str.dup); auto str2 = Foo!char('a', str); } ``` SDB 79
Jan 28 2023
On Sunday, 29 January 2023 at 03:29:45 UTC, FeepingCreature wrote:No, I mean ``` num1 = num2; onlineapp.d(22): Error: cannot modify struct instance `num1` of type `Foo!int` because it contains `const` or `immutable` members ```Sorry for writing an answer without doing the necessary unit tests. I forgot to put the type in parentheses. Here is the corrected version: ```d struct S(T) { import std.conv : to; import std.traits : ImmutableOf; immutable(T)[] data; this(R)(R[] data) { this.data = data.to!(ImmutableOf!T[]); } string toString() const { import std.format : format; return format("%s: %s", typeid(data), data); } } unittest { S!char test1, test2; alias TestType = typeof(test1.data); import std.traits : isSomeString; assert(is(TestType == string) && isSomeString!TestType); test1 = S!char("bcd"); test1.data ~= "234"; test2 = test1; assert(test1 == test2); } void main() { import std.stdio; string str = "bcd"; auto str1 = S!char(str.dup); auto str2 = S!char("abc"); str1 = str2; str1.writeln; // const(immutable(char)[]): abc str2.writeln; // const(immutable(char)[]): abc } ``` SDB 79
Jan 29 2023
On Sunday, 29 January 2023 at 17:41:26 UTC, Salih Dincer wrote:On Sunday, 29 January 2023 at 03:29:45 UTC, FeepingCreature wrote:I think you're missing the concept a bit: ``` test1 = S!char("bcd"); test1.data ~= "234"; This should emphatically *not* work. You're mutating a field of `test1`. That's what we don't want to ever happen! Instead, you should have to write `test1 = test1(test1.data ~ "234");` This looks extremely similar, but the key is that the assignment has to go through the constructor. That way, `test1` can only go from "valid value" to "valid value".No, I mean ``` num1 = num2; onlineapp.d(22): Error: cannot modify struct instance `num1` of type `Foo!int` because it contains `const` or `immutable` members ```Sorry for writing an answer without doing the necessary unit tests. I forgot to put the type in parentheses. Here is the corrected version: ```d struct S(T) { import std.conv : to; import std.traits : ImmutableOf; immutable(T)[] data; this(R)(R[] data) { this.data = data.to!(ImmutableOf!T[]); } string toString() const { import std.format : format; return format("%s: %s", typeid(data), data); } } unittest { S!char test1, test2; alias TestType = typeof(test1.data); import std.traits : isSomeString; assert(is(TestType == string) && isSomeString!TestType); test1 = S!char("bcd"); test1.data ~= "234"; test2 = test1; assert(test1 == test2); } void main() { import std.stdio; string str = "bcd"; auto str1 = S!char(str.dup); auto str2 = S!char("abc"); str1 = str2; str1.writeln; // const(immutable(char)[]): abc str2.writeln; // const(immutable(char)[]): abc } ``` SDB 79
Jan 29 2023
On Sunday, 29 January 2023 at 20:48:11 UTC, FeepingCreature wrote:This should emphatically *not* work. You're mutating a field of `test1`. That's what we don't want to ever happen! Instead, you should have to write `test1 = test1(test1.data ~ "234");` This looks extremely similar, but the key is that the assignment has to go through the constructor. That way, `test1` can only go from "valid value" to "valid value".In order to use it as you say, the following opCall() should be implemented: ```d this(R)(R[] data) { opCall(data); } alias opCall this; property opCall() inout { return data; } property opCall(R)(R[] data) { return this.data = data.to!(ImmutableOf!T[]); } ``` Then it works like this: ```d immutable arr = [ 1, 2, 3]; auto num1 = S!int(arr.dup); auto num2 = S!int(arr); assert(is(typeof(num1) == S!int)); assert(is(typeof(num1.data) == immutable(int)[])); num1(num1 ~ 4); // or: num1 = num1 ~ 5; assert(num1.length == 5); assert(num1.length > num2.length); num2 = num1; assert(num1.length == num2.length); ``` But `num1 ~= 6` doesn't work because it doesn't have an overload:Error: cannot append type `int` to type `S!int`SDB 79
Jan 29 2023
On Monday, 30 January 2023 at 03:09:14 UTC, Salih Dincer wrote:But `num1 ~= 6` doesn't work because it doesn't have an overload:But what prevents `num1.field ~= 6`? Making it private and giving it an accessor. And then you're at this: ``` struct Struct { ConstRead private int[] field_; mixin(GenerateAll); } ``` Which is *exactly* what we're trying to get away from because it's too much overhead! So I appreciate your suggestions, but I hope it's clear that they don't compete with ``` rvalue struct Struct { int[] field; } ```Error: cannot append type `int` to type `S!int`SDB 79
Jan 29 2023
On Wednesday, 25 January 2023 at 16:23:51 UTC, FeepingCreature wrote:Before I take on the effort of writing up and submitting a DIP, let me solicit feedback and see if anyone can see a reason why this idea is dumb and doesn't work. tl;dr: `immutable struct` was a mistake: it's too weak. `rvalue struct` is what we really want.Isn't this what private/` system` (DIP1035) fields with public/` trusted` getter functions are for?
Jan 30 2023
On Monday, 30 January 2023 at 13:53:53 UTC, Dukc wrote:On Wednesday, 25 January 2023 at 16:23:51 UTC, FeepingCreature wrote:Yes, the behavior of "private fields with public getters" is exactly what we're aiming for. (` system` - not really; we're not interested in doing un ` safe` things to them. This is purely about functional programming style.) Having an in-language way to declare "immutable rvalue everything" would pose some advantages over getters though: - we wouldn't have to figure out when we'd need to dup a field on access to avoid mutation-at-a-distance: sSince immutable is transitive, the answer is "never", which also makes the GC happy - we avoid the template overhead of having to generate accessors for every field, with all the required analysis to figure out attributes etc. - even struct methods cannot mutate fields, for maximum purity - it just looks nicer.Before I take on the effort of writing up and submitting a DIP, let me solicit feedback and see if anyone can see a reason why this idea is dumb and doesn't work. tl;dr: `immutable struct` was a mistake: it's too weak. `rvalue struct` is what we really want.Isn't this what private/` system` (DIP1035) fields with public/` trusted` getter functions are for?
Jan 30 2023
On Monday, 30 January 2023 at 14:10:39 UTC, FeepingCreature wrote:- we wouldn't have to figure out when we'd need to dup a field on access to avoid mutation-at-a-distance: sSince immutable is transitive, the answer is "never", which also makes the GC happyThis can be accomplished by making return value of the getter `const`.- we avoid the template overhead of having to generate accessors for every field, with all the required analysis to figure out attributes etc. - even struct methods cannot mutate fields, for maximum purity - it just looks nicer.These are all true. However, the proposed feature just feels inconsistent with how the rest of the language works. I can't pinpoint exactly why, but I think it has to do with that Immutability and visibility are usually treated as separate issues. This just does not play togeteher with rest of the language. To be fair, it wouldn't be the first feature to be like that. `protected` and `lazy` for example also feel out of place IMO. I feel you use D very differently than I do. I bump to these issues rarely enough that I don't feel bothered at all to write getters when I do. Plus I consider the struct itself (and rest of the module) being able to modify the field mostly a good thing. I wonder how people feel in general. If your style is widely spread, there might indeed be a case for a language feature.
Jan 30 2023
On Monday, 30 January 2023 at 14:43:21 UTC, Dukc wrote:I feel you use D very differently than I do. I bump to these issues rarely enough that I don't feel bothered at all to write getters when I do. Plus I consider the struct itself (and rest of the module) being able to modify the field mostly a good thing. I wonder how people feel in general. If your style is widely spread, there might indeed be a case for a language feature.That's fair. We're coming at D like it's a functional language at heart with mutability bolted on, rather than a mutable language which can borrow some functional idioms. See my 2019 DConf talk https://www.youtube.com/watch?v=nKMOFaAdtAc ; the basic summary of how we use the language still holds. The key insight is that we treat D like a functional language at the domain level, like an object-oriented language at the application level, and autogenerate everything at the infrastructure level. Almost all our structs live at the domain level, meaning they don't actually own state, they just represent domain knowledge. State-ownership happens at the application level, ie. in classes. I do feel like `std.algorithm`, `std.functional` and ranges in general invite this sort of pure-data modelling though.
Jan 30 2023
On Monday, 30 January 2023 at 15:21:02 UTC, FeepingCreature wrote:The key insight is that we treat D like a functional language at the domain level, like an object-oriented language at the application level, and autogenerate everything at the infrastructure level. Almost all our structs live at the domain level, meaning they don't actually own state, they just represent domain knowledge. State-ownership happens at the application level, ie. in classes.I need to emphasize this point. For instance, say that a struct represents "the arrival of a train at a station at a certain time." When we find that the train has a delay, we could mutate the `arrival.time` field on that struct. But what would that mean? The information given was *accurate* at the time. The struct, representing our knowledge at a certain point in time, is still valid. We have now gained new information, and wish to now replace it with an alternative value that has a different arrival time. But we don't want to *change the original,* ever, by any means! The original value is and remains a legitimate representation of the domain. That's why we don't use ref, or pointers, or state mutation in structs. Instead, we "patch" the variable with something like Haskell arrows [1]: ``` alias updateTimes = visit => visit .rebuild!(a => a.arrival = newArrivalTime) .rebuild!(a => a.departure = newDepartureTime); return visits.map!(visit => visit.id == visitId ? updateTimes(visit) : visit); ``` or some such. The goal of marking structs `immutable` (or `rvalue`) is to make any idiom that does not operate like this fail to compile. [1] https://en.wikibooks.org/wiki/Haskell/Understanding_arrows
Jan 30 2023
On Monday, 30 January 2023 at 15:31:59 UTC, FeepingCreature wrote:I need to emphasize this point. For instance, say that a struct represents "the arrival of a train at a station at a certain time." When we find that the train has a delay, we could mutate the `arrival.time` field on that struct. But what would that mean? The information given was *accurate* at the time. The struct, representing our knowledge at a certain point in time, is still valid. We have now gained new information, and wish to now replace it with an alternative value that has a different arrival time. But we don't want to *change the original,* ever, by any means! The original value is and remains a legitimate representation of the domain.I see. The idea about immutability is good IMO, but your ways to achieve that are different from mine. My way would be to have the original struct be `immutable`, which protects the fields, but have the value under construction be mutable with public fields. Data pointed to by any arrays or pointers in the `struct` would still be `immutable`. I don't see a need for fields to be non-referenceable since immutability protects them if the data in question is already committed.
Jan 30 2023
On Monday, 30 January 2023 at 20:15:48 UTC, Dukc wrote:I don't see a need for fields to be non-referenceable since immutability protects them if the data in question is already committed.Yes, again, the idea is that rvalue allows *overwriting mutable variables* of this type. ``` rvalue struct S { int a; } S s; s = S(5); s = S(6); ``` Which `immutable` does not allow. And this is important because this idiom is inescapable, it will always crop up (all over std.algorithm causing compile errors for our immutable structs, cough), and in fact I increasingly think it's just *good.* It's an inherent part of D's C heritage.
Jan 30 2023
On Tuesday, 31 January 2023 at 07:31:58 UTC, FeepingCreature wrote:And this is important because this idiom is inescapable, it will always crop up (all over std.algorithm causing compile errors for our immutable structs, cough), and in fact I increasingly think it's just *good.* It's an inherent part of D's C heritage.Just to give an example, `maxElement` is actually straight up impossible to implement for every type without either of: - `rvalue struct replacing `immutable struct` - const range API (`typeof(this) next()` rather than `void popFront()`) - rebindable structs in Phobos (librebindable) - mandatory tail recursion at every optimization level. None of which D has.
Jan 30 2023
On Tuesday, 31 January 2023 at 07:31:58 UTC, FeepingCreature wrote:On Monday, 30 January 2023 at 20:15:48 UTC, Dukc wrote:In that case I'd probably go with a mutable pointer to an immutable struct. That way you can replace the value in whole, but not single fields of it.I don't see a need for fields to be non-referenceable since immutability protects them if the data in question is already committed.Yes, again, the idea is that rvalue allows *overwriting mutable variables* of this type.
Jan 31 2023
On Tuesday, 31 January 2023 at 11:52:33 UTC, Dukc wrote:On Tuesday, 31 January 2023 at 07:31:58 UTC, FeepingCreature wrote:Okay, now how do you do that without causing GC load? :) Keep in mind that we plan to use this for very inner loops of not-always-cheap algorithms.On Monday, 30 January 2023 at 20:15:48 UTC, Dukc wrote:In that case I'd probably go with a mutable pointer to an immutable struct. That way you can replace the value in whole, but not single fields of it.I don't see a need for fields to be non-referenceable since immutability protects them if the data in question is already committed.Yes, again, the idea is that rvalue allows *overwriting mutable variables* of this type.
Jan 31 2023
On Tue, Jan 31, 2023 at 01:02:57PM +0000, FeepingCreature via Digitalmars-d wrote:On Tuesday, 31 January 2023 at 11:52:33 UTC, Dukc wrote:Why would a mutable pointer to an immutable struct cause GC load? Isn't it just a matter of taking an address? struct S { ... } S s, t; immutable(S)* ptr = &s; ... ptr = &t; ... // etc T -- Truth, Sir, is a cow which will give [skeptics] no more milk, and so they are gone to milk the bull. -- Sam. JohnsonOn Tuesday, 31 January 2023 at 07:31:58 UTC, FeepingCreature wrote:Okay, now how do you do that without causing GC load? :) Keep in mind that we plan to use this for very inner loops of not-always-cheap algorithms.On Monday, 30 January 2023 at 20:15:48 UTC, Dukc wrote:In that case I'd probably go with a mutable pointer to an immutable struct. That way you can replace the value in whole, but not single fields of it.I don't see a need for fields to be non-referenceable since immutability protects them if the data in question is already committed.Yes, again, the idea is that rvalue allows *overwriting mutable variables* of this type.
Jan 31 2023
On Tuesday, 31 January 2023 at 13:02:57 UTC, FeepingCreature wrote:Okay, now how do you do that without causing GC load? :) Keep in mind that we plan to use this for very inner loops of not-always-cheap algorithms.Why can't you use regular encapsulation? I think it would be better for you to see if you can do this is as a rewrite instead as that doesn't affect the type system. to get slightly different value semantics, but as far as I can tell it is only a rewrite.
Jan 31 2023
On Tuesday, 31 January 2023 at 18:13:58 UTC, Ola Fosheim Grøstad wrote:to get slightly different value semantics, but as far as I can tell it is only a rewrite.Or maybe it isn't (shrug). Anyway, you might want to look at the https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#non-destructive-mutation
Jan 31 2023
On Tuesday, 31 January 2023 at 18:30:44 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 31 January 2023 at 18:13:58 UTC, Ola Fosheim Grøstad wrote:Yes, that's exactly what we want! What we use right now for `with` is ``` immutable struct S { int value; mixin(GenerateThis); } S s = S(5); S butFour = s.rebuild!(a => a.value = 4); ``` Which seems to work fine."class" to get slightly different value semantics, but as far as I can tell it is only a rewrite.Or maybe it isn't (shrug). Anyway, you might want to look at https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#non-destructive-mutation
Jan 31 2023
On Tuesday, 31 January 2023 at 18:13:58 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 31 January 2023 at 13:02:57 UTC, FeepingCreature wrote:Maybe it can be done as a rewrite? For instance, we used to do ``` struct S { private int a_; public int a() { return this.a_; } } ``` But this is not quite the semantic we actually want because, for instance, the accessors will still run the invariants, which is pointless because we know the fields won't ever change after the constructor call, and also the function call itself is just useless. But that's the interface we want, at least. The thing we *actually* want is, roughly, ``` struct S { private int a_; public rvalue alias a = this.a_; } ``` But then we're inventing features again. That said, even "a function that returns 'a'" isn't *quite* the right semantics, because this is valid D: ``` struct S { int a; } S foo() { ... } foo.a = 5; ``` On that topic, what the fuck, D.Okay, now how do you do that without causing GC load? :) Keep in mind that we plan to use this for very inner loops of not-always-cheap algorithms.Why can't you use regular encapsulation? I think it would be better for you to see if you can do this is as a rewrite instead as that doesn't affect the type system. to get slightly different value semantics, but as far as I can tell it is only a rewrite.
Jan 31 2023
On Wednesday, 1 February 2023 at 07:46:24 UTC, FeepingCreature wrote:Maybe it can be done as a rewrite?[…]But this is not quite the semantic we actually want because, for instance, the accessors will still run the invariants, which is pointless because we know the fields won't ever change after the constructor call, and also the function call itself is just useless. But that's the interface we want, at least.Hm, anyway, if you can do it as a rewrite then you should be able to implement it in the parser and in the runtime. That way you can maintain it even if it isn't added to the core D language. (If you are interested in something like this, just send me an email.)
Feb 01 2023
On Wednesday, 1 February 2023 at 09:29:01 UTC, Ola Fosheim Grøstad wrote:On Wednesday, 1 February 2023 at 07:46:24 UTC, FeepingCreature wrote:I mean, since it's a *reduction* in capability, it would even be enough to do it as an audit step. Error on any assignment to a field in the struct marked with a UDA. That can't be done before semantic assignment though...Maybe it can be done as a rewrite?[…]But this is not quite the semantic we actually want because, for instance, the accessors will still run the invariants, which is pointless because we know the fields won't ever change after the constructor call, and also the function call itself is just useless. But that's the interface we want, at least.Hm, anyway, if you can do it as a rewrite then you should be able to implement it in the parser and in the runtime. That way you can maintain it even if it isn't added to the core D language. (If you are interested in something like this, just send me an email.)
Feb 01 2023
On Wednesday, 1 February 2023 at 11:26:17 UTC, FeepingCreature wrote:I mean, since it's a *reduction* in capability, it would even be enough to do it as an audit step. Error on any assignment to a field in the struct marked with a UDA. That can't be done before semantic assignment though...Yes, if you can find a single location that you can hook into and then put all the required changes in separate (new) files to keep it maintainable. (The trick is to make as few changes as possible to files that keep changing upstream, otherwise rebasing becomes a tedious chore in my experience).
Feb 01 2023
On Wednesday, 25 January 2023 at 16:23:51 UTC, FeepingCreature wrote:Please, stop adding new features.
Jan 31 2023
On Wednesday, 25 January 2023 at 16:23:51 UTC, FeepingCreature wrote:D libraries like to behave like they can declare variables and assign values to them. (Oh, to live in such innocence!) This is all over Phobos, Dub, etc. `immutable struct` frustrates this belief. Because you could always take the address of a field, which would be `immutable T*`, you could see the value changing when you overwrite the variable - a constness violation. `immutable` solves this by preventing you from modifying the memory of the field while the pointer lives. This largely doesn't work, because people don't test with `immutable struct` in the first place. If an `rvalue struct` is used, the naive code works as before, but the type gets the correctness benefits of immutable: you can only construct a new value through the constructor.Maybe escape analysis could be used as an alternative. If the compiler can prove that no pointer to a local variable or its members could escape, then it should be safe to reassign to the variable. With DIP1000 the compiler should be able to determine this.
Feb 01 2023