digitalmars.dip.ideas - Single Assignment
- Walter Bright (37/37) Nov 14 Single assignment is the idea that a variable can only be assigned to on...
- Richard (Rikki) Andrew Cattermole (26/26) Nov 14 Originally I was going to ask the question if its just an alias to
- Walter Bright (2/2) Nov 14 Headconst as a storage class for the variable doesn't really work, becau...
- Richard (Rikki) Andrew Cattermole (15/17) Nov 14 Hang on:
- Walter Bright (3/27) Nov 14 or allowed at all.
- monkyyy (16/17) Nov 14 My ct mutability features are often forced to be; it doesnt
- Dmitry Olshansky (5/41) Nov 14 final seems like a good candidate for this. Considering it’s a
- Walter Bright (7/10) Nov 14 Consider:
- Walter Bright (1/1) Nov 14 Yes, final is a good idea.
- Jonathan M Davis (56/60) Nov 14 I use it fairly frequently, but at the end of the day, I just do what se...
- Walter Bright (3/3) Nov 14 On a short function, I agree that any compiler enforcement of single ass...
- Lance Bachmeier (10/15) Nov 14 No. I rarely use const because dealing with transitivity is far
- jmh530 (6/22) Nov 14 It seems like your concern is more about making it easier for
- Walter Bright (5/9) Nov 14 There is an issue with whether single assignment declaration should be t...
- Dukc (22/27) Nov 14 Mostly yes. If a variable is logically a variable (like some sort
- Walter Bright (3/12) Nov 14 `alias` is a distinct feature from single assignment.
- Meta (20/26) Nov 14 1. Always, unless there's a good reason not to.
- Paul Backus (10/21) Nov 14 I assume this also means that non-const struct and class methods
- Walter Bright (7/8) Nov 14 One saving grace is you can write:
Single assignment is the idea that a variable can only be assigned to once. This idea has been around for a while as recommended style, as in: Bad style: ```d int a = 3; a = 4; ``` Good style: ```d int a = 3; int b = 4; ``` It's good style because it improves the comprehensibility of the code. The bad style was good when compiler did not do register allocation by live range analysis, so re-using variables was a good idea. But compilers are pretty good at that now, so that is no longer an issue. Single assignment has become popular in other languages. In D, we can approach this with `const`: ```d const int a = 3; a = 4; // error: a is const ``` But there is a problem: ```d int a = 3; const int* p = &a; *p = 4; // error a = 4; // ok ``` This is because `const` is transitive. Allowing things like a "const pointer to mutable" would be quite disruptive to the D type system, and may wind up being excessively complex. So, some food for thought: 1. do you use single assignment in D? (I try to use it.) 2. is const, with its transitivity, good enough? 3. if not good enough, is there a practical way for D to enforce it? 4. is this a waste of time chasing rainbows?
Nov 14
Originally I was going to ask the question if its just an alias to
another variable why would you need another variable?
But then I remembered ternary operator.
```d
int val = gate ? arg1 : anotherVar;
```
Essentially what you have proposed is headconst that is a storage class
not a type qualifier.
Thinking along the lines of by-ref storage classes, we could write it
like so:
```d
int* ptr = new int;
func(ptr);
headconst val = new int;
func(val);
void func(headconst int val) {
}
```
Basic types don't need this, they'll cast off const quite happily.
Lazy also needs a similar treatment so it can only be passed around if
the variable is marked as lazy.
I don't have answers as to if it would be a good idea to add, but I
would use it.
On another related note, I want the effect effectivelyconst modeled to
help with a borrow checker. I don't think it can be incorporated into
this just related.
Nov 14
Headconst as a storage class for the variable doesn't really work, because a pointer to the variable can change it.
Nov 14
On 15/11/2025 7:21 AM, Walter Bright wrote:Headconst as a storage class for the variable doesn't really work, because a pointer to the variable can change it.Hang on: ```d int* ptr0 = new int; headconst int ptr1 = ptr0; auto ptr2 = &ptr1; assert(ptr2 is ptr0); ``` How do you get a pointer to the storage of the pointer? Why would the following work? ```d headconst int ptr1 = ...; int** ptr3 = &&ptr1; ``` I'm not sure taking a pointer to that var should be allowed in safe code.
Nov 14
On 11/14/2025 10:26 AM, Richard (Rikki) Andrew Cattermole wrote:On 15/11/2025 7:21 AM, Walter Bright wrote:Take the address of a pointer.Headconst as a storage class for the variable doesn't really work, because a pointer to the variable can change it.Hang on: ```d int* ptr0 = new int; headconst int ptr1 = ptr0; auto ptr2 = &ptr1; assert(ptr2 is ptr0); ``` How do you get a pointer to the storage of the pointer?Why would the following work? ```d headconst int ptr1 = ...; int** ptr3 = &&ptr1; ``` I'm not sure taking a pointer to that var should be allowed in safe code.or allowed at all.
Nov 14
On Friday, 14 November 2025 at 08:19:28 UTC, Walter Bright wrote:1. do you use single assignment in D? (I try to use it.)My ct mutability features are often forced to be; it doesnt belong in runtime code. If its possible to compute something in one call, it shouldn't ever even exist. One could imagine a iterator api in oo where `empty` *had to* be a bool and not an bool function, and it becoming a problem if code with a ref to a range read that empty held onto the reference and then it changed under it (such as me appending to it). In template hell, empty is the users function and it gets inlined where its needed, in oo I bet it wouldve eventually been a "problematic source of bugs" that "we all need to enforce invarents of" and if there was something that slipped thru and needed backwards compatibility such oo programmers would try to make "atomic reads" of this empty and drastically expanding the numbers of reads, thinking they are the best at following a "single source of truth principal".
Nov 14
On Friday, 14 November 2025 at 08:19:28 UTC, Walter Bright wrote:Single assignment is the idea that a variable can only be assigned to once. This idea has been around for a while as recommended style, as in: Bad style: ```d int a = 3; a = 4; ``` Good style: ```d int a = 3; int b = 4; ``` It's good style because it improves the comprehensibility of the code. The bad style was good when compiler did not do register allocation by live range analysis, so re-using variables was a good idea. But compilers are pretty good at that now, so that is no longer an issue. Single assignment has become popular in other languages. In D, we can approach this with `const`: ```d const int a = 3; a = 4; // error: a is const ``` But there is a problem: ```d int a = 3; const int* p = &a; *p = 4; // error a = 4; // ok ``` This is because `const` is transitive. Allowing things like a "const pointer to mutable" would be quite disruptive to the D type system, and may wind up being excessively complex.final seems like a good candidate for this. Considering it’s a storage class it may not disrupt the type system all that much. Java’s final is basically this. Scala/Kotlin val is implicitly final.
Nov 14
On 11/14/2025 3:25 AM, Dmitry Olshansky wrote:final seems like a good candidate for this. Considering it’s a storage class it may not disrupt the type system all that much. Java’s final is basically this. Scala/Kotlin val is implicitly final.Consider: ```d final int a = 3; int* p = &a; *p = 4; // allowed or not? ```
Nov 14
On Friday, November 14, 2025 1:19:28 AM Mountain Standard Time Walter Bright via dip.ideas wrote:1. do you use single assignment in D? (I try to use it.)I use it fairly frequently, but at the end of the day, I just do what seems to make the most sense for the code. Typically, I'll reuse a variable when the nature of the variable is such that reuse makes sense, but I don't typically use the same variable for different things. But I don't typically use const for any of this. I just don't reassign the variable, and if you're dealing with more complex objects, the variable is usually going to be mutated even if you don't assign it a new value - e.g. I typically won't reassign a variable that's a range, but iterating it mutates it. So, if by single assignment, the expectation is a lack mutability (be it for the object sitting on the stack or what it refers to), then I very rarely use it. But if the idea is just to use separate variables for separate things rather than reusing an existing variable, then I do that frequently.2. is const, with its transitivity, good enough?If you're trying to enforce head-const, then const really doesn't work because of transitivity.3. if not good enough, is there a practical way for D to enforce it?If the goal is to avoid having the variable be reassigned but not necessarily care about the general mutability of the object, then it can be enforced with a wrapper struct which disallows assignment while forwarding all of the other calls to the object itself, though that does get in the way of passing the variable to other functions. We could theoretically add a new storage class which dealt with it - presumably one which disallowed assignment but allowed you to call member functions, and you could pass it by value (since that would copy) but not by ref unless that ref were const or inout (since otherwise, it could be assigned to). But with the language as it stands, the best way way to do it is to use a wrapper structs, which some folks have done before.4. is this a waste of time chasing rainbows?I'm inclined to think so. Honestly, if it weren't for the fact that immutable is implicitly shared and the fact that that can be extremely useful, I'd be tempted to argue that both const and immutable are already a waste of time in general, because they're just too restrictive once you're dealing with actual objects rather than simple examples with primitive types. So, adding even more types of constness seems like a move in the wrong direction to me. It might not be as bad if it's just a storage class and not a type qualifier, but it's still more complexity for something where I don't see much value in having the compiler enforce anything. If you don't want to reassign a variable, you can just choose to not reassign it - and unless your function is hugely long, it's not like that would be hard to keep track of. Maybe there would be some value in being able to enforce it programmatically without needing a wrapper type, but it would also complicate the language further, and personally, I think that the benefit is minimal enough to not merit the additional complexity. Also, I'm not sure what practical benefit there really is to enforcing single assignment anyway. It can make it easier to understand the code, but why would the compiler need to try to enforce any kind of understandability? Especially when it's trivial for the programmer to get it right if that's what they want. It sounds closer to enforcing formatting rules or trying to require informative variable names than to doing anything practical with regards to compiling code. If anything, I think that the major hole with regards to type qualifiers in D is the inability to have tail-modifiers with class references without some kind of wrapper struct which does casting to make it happen. The workarounds for the lack of tail-const, tail-immutable, or tail-shared with classes are ugly and difficult to get right (and pretty questionable with regards to type correctness). - Jonathan M Davis
Nov 14
On a short function, I agree that any compiler enforcement of single assignment is pointless. But for a longer one, it's a good documentation aid. I like your idea of using `final` to trigger it. Thanks!
Nov 14
On Friday, 14 November 2025 at 08:19:28 UTC, Walter Bright wrote:1. do you use single assignment in D? (I try to use it.)This is my first choice.2. is const, with its transitivity, good enough?No. I rarely use const because dealing with transitivity is far worse than any benefit from using const.3. if not good enough, is there a practical way for D to enforce it?As Dmitry said, Scala's val does this, and I've always wished we had it. val also prohibits the use of default values, so `val x: Int;` is not allowed. You have to specify `val x: Int = 6;` or `val x = 6;`.4. is this a waste of time chasing rainbows?No. It would be one of those additions that make the language more fun to use.
Nov 14
On Friday, 14 November 2025 at 08:19:28 UTC, Walter Bright wrote:Single assignment is the idea that a variable can only be assigned to once. This idea has been around for a while as recommended style, as in: Bad style: ```d int a = 3; a = 4; ``` Good style: ```d int a = 3; int b = 4; ``` It's good style because it improves the comprehensibility of the code. [snip]It seems like your concern is more about making it easier for people to write code in a single assignment way, rather than preventing people from using the "bad style" way. I don't have an issue with that. Sometimes the "bad style" is good enough for a quick and dirty script.
Nov 14
On 11/14/2025 9:34 AM, jmh530 wrote:It seems like your concern is more about making it easier for people to write code in a single assignment way, rather than preventing people from using the "bad style" way. I don't have an issue with that. Sometimes the "bad style" is good enough for a quick and dirty script.There is an issue with whether single assignment declaration should be the default, and a "mutable" declaration should require a keyword. Unfortunately, there is so much water under that bridge that defaulting to single assignment would be overly disruptive. I doubt even editions could save it!
Nov 14
On Friday, 14 November 2025 at 08:19:28 UTC, Walter Bright wrote:1. do you use single assignment in D? (I try to use it.)Mostly yes. If a variable is logically a variable (like some sort of a counter for a loop) then I'll re-assign it. But for intermediate values for calculations, singe assignment only, usually.2. is const, with its transitivity, good enough?Not as a general purpose tool. You could use it when you don't have indirections, or when you aren't going to mutate the pointed data anyway, but this isn't nearly always. Personally, I use `immutable`/`const` only when the pointed-to data is read-only.3. if not good enough, is there a practical way for D to enforce it?Limiting the lifetime of variable for when it's actually supposed to be used sometimes makes sense. I sometimes even write `if(true){ /*...*/ }` blocks for that. I know that a block statement without `if(true)` would work just as well, but I feel that the blocks are easier to distinguish visually from the surrounding code when they have a header.4. is this a waste of time chasing rainbows?Not necessarily for linting tools, but I don't think it's worth to add a language-level feature for. However, improving local functions and/or `alias` so they can be used in place of single assignment variables more often, or language level tuples with pattern matching, so you can do `auto a, b = {/* define and return a and b... */}();` could be worth it.
Nov 14
On 11/14/2025 9:37 AM, Dukc wrote:Limiting the lifetime of variable for when it's actually supposed to be used sometimes makes sense. I sometimes even write `if(true){ /*...*/ }` blocks for that. I know that a block statement without `if(true)` would work just as well, but I feel that the blocks are easier to distinguish visually from the surrounding code when they have a header.Hmm, interesting stylistic choice. I never thought of that!However, improving local functions and/or `alias` so they can be used in place of single assignment variables more often, or language level tuples with pattern matching, so you can do `auto a, b = {/* define and return a and b... */}();` could be worth it.`alias` is a distinct feature from single assignment.
Nov 14
On Friday, 14 November 2025 at 08:19:28 UTC, Walter Bright wrote:So, some food for thought: 1. do you use single assignment in D? (I try to use it.) 2. is const, with its transitivity, good enough? 3. if not good enough, is there a practical way for D to enforce it? 4. is this a waste of time chasing rainbows?1. Always, unless there's a good reason not to. 2. Nope. 3. Ideally the default would be flipped a la Rust or Swift, where head const is the default, and variables must be marked as mutable to be mutated. 4. Final as a storage class like Dmitry suggested would probably be the easiest and fastest way to do it, so the cost would be low, but the benefit would also be very low. As for whether a pointer to a "final" value should be able to modify the value it points to, I'd say yes. We already have transitive const and immutable, so the programmer can choose their level of strictness: final, which just prevents reassignment and is not transitive, const, which prevents any modification whatsoever, but does not guarantee that the value won't be modified by other threads, and immutable, which is transitive, and prevents modification by *any* thread. However, having to write final everywhere would be really annoying. I rarely use it in Java and just trust myself to be disciplined and adhere to the single assignment principle.
Nov 14
On Friday, 14 November 2025 at 18:53:05 UTC, Meta wrote:4. Final as a storage class like Dmitry suggested would probably be the easiest and fastest way to do it, so the cost would be low, but the benefit would also be very low. As for whether a pointer to a "final" value should be able to modify the value it points to, I'd say yes. We already have transitive const and immutable, so the programmer can choose their level of strictness: final, which just prevents reassignment and is not transitive, const, which prevents any modification whatsoever, but does not guarantee that the value won't be modified by other threads, and immutable, which is transitive, and prevents modification by *any* thread.I assume this also means that non-const struct and class methods called on a final variable are allowed to mutate it, via the 'this' reference. Frankly, this seems like such a weak guarantee that I cannot imagine it being of any practical use. When you see a reference to a const variable, you can always find out what its value is by looking back at its declaration, without reading any of the code in between. With final, you would still have to read all of that intervening code to see if there are any indirect mutations.
Nov 14
Good points! On 11/14/2025 10:53 AM, Meta wrote:However, having to write final everywhere would be really annoying.One saving grace is you can write: ``` final abc = 3; ``` as `abc` is inferred to be `int`.
Nov 14









Walter Bright <newshound2 digitalmars.com> 