www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - C is Brittle D is Plastic

reply Walter Bright <newshound2 digitalmars.com> writes:
It's true that writing code in C doesn't automatically make it faster.

For example, string manipulation. 0-terminated strings (the default in C) are, 
frankly, an abomination. String processing code is a tangle of strlen, strcpy, 
strncpy, strcat, all of which require repeated passes over the string looking 
for the 0. (Even worse, reloading the string into the cache just to find its 
length makes things even slower.)

Worse is the problem that, in order to slice a string, a malloc is needed to 
copy the slice to. And then carefully manage the lifetime of that slice.

The fix is simple - use length-delimited strings. D relies on them to great 
effect. This can be done in C, but there is no succor from the language, and 
such a package is not standardized. I've proposed a simple enhancement for C to 
make them work https://www.digitalmars.com/articles/C-biggest-mistake.html but 
nobody in the C world has any interest in it (which is baffling, as it is so 
simple!).

Another source of slowdown in C that became apparent over the years is C is a 
brittle language, rather than a plastic one. The first algorithm selected for a 
C project gets so welded into it that it cannot be changed without great 
difficulty. (And we all know that algorithms are the key to speed, not coding 
details.) Why does this happen with C?

It's because one cannot switch back and forth between a reference type and a 
value type without extensively rewriting every use of it. For example:

```C
struct S { int a; }
int foo(struct S s) { return s.a; }
int bar(struct S *s) { return s->a; }
```
To switch between reference and value, it's necessary to go through all the
code 
swapping . and ->. It's just too tedious and never happens. In D:
```D
struct S { int a; }
int foo(S s) { return s.a; }
int bar(S *s) { return s.a; }
```
Working on D shows that there is no reason for the C and C++ -> operator to
even 
exist, the . operator covers both bases!
Mar 21
next sibling parent reply Dennis <dkorpel gmail.com> writes:
On Sunday, 22 March 2026 at 04:47:41 UTC, Walter Bright wrote:
 Another source of slowdown in C that became apparent over the 
 years is C is a brittle language, rather than a plastic one.
Another example where D shines in this regard is UFCS allowing you to turn fields into methods and vice versa: ```D struct S { ubyte* ptr; ubyte* end; size_t length() => end - ptr; } ``` ```D struct S { ubyte* ptr; size_t length; ubyte* end() => ptr + length; } ``` You don't need to change `.end` into `.end()`, `.end` works on both fields and methods.
Mar 22
next sibling parent reply Sergey <kornburn yandex.ru> writes:
On Sunday, 22 March 2026 at 11:56:00 UTC, Dennis wrote:
 You don't need to change `.end` into `.end()`, `.end` works on 
 both fields and methods.
Some languages consider this as a downside. Because of clarity for human or llm who is reading the code - which is trying to understand what is it method or field, structure or pointer to the structure..
Mar 22
next sibling parent reply Kapendev <alexandroskapretsos gmail.com> writes:
On Sunday, 22 March 2026 at 12:23:28 UTC, Sergey wrote:
 On Sunday, 22 March 2026 at 11:56:00 UTC, Dennis wrote:
 You don't need to change `.end` into `.end()`, `.end` works on 
 both fields and methods.
Some languages consider this as a downside. Because of clarity for human or llm who is reading the code - which is trying to understand what is it method or field, structure or pointer to the structure..
The same can be said for replacing the `->` operator with a `.`. For example: `int a = variable.value;` Is `variable` a pointer or a number in this example? You don't know. Do you care? No, and if you do then you should know what your code is doing in the first place lol
Mar 22
parent reply Sergey <kornburn yandex.ru> writes:
On Sunday, 22 March 2026 at 15:05:41 UTC, Kapendev wrote:
 The same can be said for replacing the `->` operator with a `.`.
 For example: `int a = variable.value;`
 Is `variable` a pointer or a number in this example?
 You don't know. Do you care? No, and if you do then you should 
 know what your code is doing in the first place lol
This is wrong example Kap. Imagine you have a function.. with many rows. And then you a calling another function there like: ```d // ... many rows above another_func(foo.bar, foo.baz, x); // ... many rows below ``` here for x parameter you need to give a reference to foo. But you don't know which type foo now - is it a pointer or not.. You will have to go somewhere above search the foo somewhere - then go back to another_func and change the x..
Mar 22
next sibling parent Sergey <kornburn yandex.ru> writes:
On Sunday, 22 March 2026 at 16:32:25 UTC, Sergey wrote:
 here for x parameter you need to give a reference to foo. But 
 you don't know which type foo now - is it a pointer or not..
 You will have to go somewhere above search the foo somewhere - 
 then go back to another_func and change the x..
Of course partially tooling is solving this - with properly configured editor - you could see it easier But there are other more complicated examples (with templates involved) where tooling won't help you
Mar 22
prev sibling parent Kapendev <alexandroskapretsos gmail.com> writes:
On Sunday, 22 March 2026 at 16:32:25 UTC, Sergey wrote:
 This is wrong example Kap.

 Imagine you have a function.. with many rows.
 And then you a calling another function there like:
 ```d
 // ... many rows above
 another_func(foo.bar, foo.baz, x);
 // ... many rows below
 ```

 here for x parameter you need to give a reference to foo. But 
 you don't know which type foo now - is it a pointer or not..
 You will have to go somewhere above search the foo somewhere - 
 then go back to another_func and change the x..
You will get a compiler error telling you the type. It's the same thing.
Mar 22
prev sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Sunday, 22 March 2026 at 12:23:28 UTC, Sergey wrote:
 On Sunday, 22 March 2026 at 11:56:00 UTC, Dennis wrote:
 You don't need to change `.end` into `.end()`, `.end` works on 
 both fields and methods.
Some languages consider this as a downside. Because of clarity for human or llm who is reading the code - which is trying to understand what is it method or field, structure or pointer to the structure..
and it’s considered a win for each. Neither human nor AI should care if it’s a property or a field. The whole point of properties is being largely equivalent to fields when used. The rather annoying thing about D is that properties aren’t really first-class, but piggy-backed on functions.
Mar 25
next sibling parent Sergey <kornburn yandex.ru> writes:
On Wednesday, 25 March 2026 at 13:20:16 UTC, Quirin Schroll wrote:

 and it’s considered a win for each. Neither human nor AI should 
 care if it’s a property or a field. The whole point of 
 properties is being largely equivalent to fields when used. The 
 rather annoying thing about D is that properties aren’t really 
 first-class, but piggy-backed on functions.
For example languages like Rust, Go, and C++ deliberately avoid property syntax because: - It hides computation behind field access - Makes performance less obvious - Reduces clarity (is it a field or a function?)
Mar 25
prev sibling parent Sergey <kornburn yandex.ru> writes:
On Wednesday, 25 March 2026 at 13:20:16 UTC, Quirin Schroll wrote:

 and it’s considered a win for each. Neither human nor AI should 
 care if it’s a property or a field. The whole point of 
 properties is being largely equivalent to fields when used. The 
 rather annoying thing about D is that properties aren’t really 
 first-class, but piggy-backed on functions.
Also Zig mentioned it explicitly: https://ziglang.org/learn/why_zig_rust_d_cpp/ So nothing odd
Mar 25
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
Nice example!
Mar 22
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Sunday, 22 March 2026 at 11:56:00 UTC, Dennis wrote:
 On Sunday, 22 March 2026 at 04:47:41 UTC, Walter Bright wrote:
 Another source of slowdown in C that became apparent over the 
 years is C is a brittle language, rather than a plastic one.
Another example where D shines in this regard is UFCS allowing you to turn fields into methods and vice versa: ```D struct S { ubyte* ptr; ubyte* end; size_t length() => end - ptr; } ``` ```D struct S { ubyte* ptr; size_t length; ubyte* end() => ptr + length; } ``` You don't need to change `.end` into `.end()`, `.end` works on both fields and methods.
That isn’t UFCS, that’s just empty parentheses being optional. Those are completely orthogonal language concepts.
Mar 25
prev sibling next sibling parent reply Derek Fawcus <dfawcus+dlang employees.org> writes:
On Sunday, 22 March 2026 at 04:47:41 UTC, Walter Bright wrote:
 It's because one cannot switch back and forth between a 
 reference type and a value type without extensively rewriting 
 every use of it. For example:

 ```C
 struct S { int a; }
 int foo(struct S s) { return s.a; }
 int bar(struct S *s) { return s->a; }
 ```
 To switch between reference and value, it's necessary to go 
 through all the code swapping . and ->.
Not really, if one has a little for-thought, and commits to accessing the members via ->, the two become interchangeable, with no performance impact. ```c int inner1 (struct foo fa) { struct foo *fp = &fa; fp->a += 1; return fp->a + 1; } ``` One can also adapt in the other direction if one started with pointer arguments (add const as desired): ```c int inner3 (struct foo *fp) { struct foo fa = *fp; fa.a += 1; return fa.a + 1; } ``` DF
Mar 22
parent reply Derek Fawcus <dfawcus+dlang employees.org> writes:
On Sunday, 22 March 2026 at 17:08:05 UTC, Derek Fawcus wrote:
 On Sunday, 22 March 2026 at 04:47:41 UTC, Walter Bright wrote:
 To switch between reference and value, it's necessary to go 
 through all the code swapping . and ->.
Not really, if one has a little for-thought, and commits to accessing the members via ->, the two become interchangeable, with no performance impact.
One can even play macro games (using _Generic) to make a wrapper for the function, such that one does not even need to go through and update the call sites to add or remove the & . However going back to the original point, as of late last year / early this year it is now obsolete. That specific example of switching the code to use . or -> is something which the LLM assisted coding agents are particularly adept at. Such refactoring tasks are what I've been using it for, where it is quite easy to specify some form of grep pattern and then rules to update the code. So these days that'll take less than an hour to achieve. DF
Mar 22
next sibling parent Lance Bachmeier <no spam.net> writes:
On Sunday, 22 March 2026 at 17:19:26 UTC, Derek Fawcus wrote:

 However going back to the original point, as of late last year 
 / early this year it is now obsolete.  That specific example of 
 switching the code to use . or -> is something which the LLM 
 assisted coding agents are particularly adept at.

 Such refactoring tasks are what I've been using it for, where 
 it is quite easy to specify some form of grep pattern and then 
 rules to update the code.  So these days that'll take less than 
 an hour to achieve.
If that's the standard (unlimited time and resources) then it was never really a thing. You could have always hired someone to make the changes for you. There's really no reason to use high-level programming languages at all if you're willing to make those assumptions.
Mar 22
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/22/2026 10:19 AM, Derek Fawcus wrote:
 One can even play macro games (using _Generic) to make a wrapper for the 
 function, such that one does not even need to go through and update the call 
 sites to add or remove the & .
If _Generic is needed, you have outgrown C. (I am regularly baffled by the weird useless things added to C, but the *needed* things are totally ignored.)
 However going back to the original point, as of late last year / early this
year 
 it is now obsolete.  That specific example of switching the code to use . or
-> 
 is something which the LLM assisted coding agents are particularly adept at.
D doesn't need a coding agent to refactor the code. It paid off handsomely with the Warp program I did a few years ago. I found it very easy to try different algorithms and data structures.
 Such refactoring tasks are what I've been using it for, where it is quite easy 
 to specify some form of grep pattern and then rules to update the code.  So 
 these days that'll take less than an hour to achieve.
I've done these sorts of refactorings. It takes 0 minutes, because the compiler figures it out and does it. It also doesn't wind up with a blizzard of diffs in the git history. It doesn't need code reviewing, either. As I've become more experienced, my tolerance for all the weird kludges necessary for professional C getting lower and lower. LLMs don't really solve the problem, as the wrangled C code is just as ugly looking. I've used LLMs to a modest extent, and am impressed by it. So, I might be wrong about the above. But it's fun to talk about it!
Mar 22
next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 23/03/2026 2:51 PM, Walter Bright wrote:
 As I've become more experienced, my tolerance for all the weird kludges 
 necessary for professional C getting lower and lower. LLMs don't really 
 solve the problem, as the wrangled C code is just as ugly looking.
Its not like we can talk, we've got some real uglies hiding out in our implementation. Like monitor proxy support. Uck, I'd love to gut that out of druntime and swap to an operator overload. But alas not approved, but hopefully that will change.
Mar 22
prev sibling next sibling parent Lance Bachmeier <no spam.net> writes:
On Monday, 23 March 2026 at 01:51:40 UTC, Walter Bright wrote:

 As I've become more experienced, my tolerance for all the weird 
 kludges necessary for professional C getting lower and lower. 
 LLMs don't really solve the problem, as the wrangled C code is 
 just as ugly looking.
I recently watched a YouTube video about how LLMs produce almost correct code. It's just good enough that errors slip past the code reviewers, and then down the road you're dealing with those errors as they get caught in production. It hit home, because it matches my experience. The trustworthy solution to the problem under discussion here is to have the LLM write a parser and transpile the old code to its new form. Using D and not having to decide on the trustworthy path is obviously a win.
Mar 22
prev sibling parent Derek Fawcus <dfawcus+dlang employees.org> writes:
On Monday, 23 March 2026 at 01:51:40 UTC, Walter Bright wrote:
 On 3/22/2026 10:19 AM, Derek Fawcus wrote:
 LLMs don't really solve the problem, as the wrangled C code is 
 just as ugly looking.
In the example I cited, that was not an issue. I was able to tightly specify the replacement, such that it effectively added as an editor "search and replace" facility on steroids. So the result was exactly what I would have written. I'm still in the process of evaluating LLM based agents in my workflow, but the above is one where I have concluded they are useful. The one detrimental issue being the volume of change which may result from a global application to fix a poor pattern. However, for your quoted example, that would not arise. As there would only be the one (or small set of) function(s) implementing the algorithm to be changed, together with use of _Generic hidden by a macro to catch all of the call sites. Only if that algorithmic change the proved to be valuable would it then be worth the cost of updating the call instances, and eliminating the _Generic using macro. Then there the review cost for the LLM generated diffs would be minimal. As to using an LLM agent to produce 'original' code for a project, I'm still skeptical.
Mar 23
prev sibling parent reply user1234 <user1234 12.de> writes:
On Sunday, 22 March 2026 at 04:47:41 UTC, Walter Bright wrote:
 [...]
 Working on D shows that there is no reason for the C and C++ -> 
 operator to even exist, the . operator covers both bases!
Yes from the user POV. From the compilers-details POV I think that semantics checks for that are awfully complex. Add to this your system of UFCS. Boom, 1000 lines of code to desintricate what the programmer really meant. However that's certain that once implemented properly it's 100% profit for hundreds of programmers.
Mar 23
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/23/2026 8:12 AM, user1234 wrote:
 On Sunday, 22 March 2026 at 04:47:41 UTC, Walter Bright wrote:
 [...]
 Working on D shows that there is no reason for the C and C++ -> operator to 
 even exist, the . operator covers both bases!
Yes from the user POV. From the compilers-details POV I think that semantics checks for that are awfully complex. Add to this your system of UFCS. Boom, 1000 lines of code to desintricate what the programmer really meant. However that's certain that once implemented properly it's 100% profit for hundreds of programmers.
I admit the implementation code is not a dreamboat. But in defense, in the 1980s I attempted to develop the be-all and end-all windowing user interface library. I soon discovered that what is intuitive and easy for the user is a giant mess of special cases in the implementation. And when the implementation was simple and consistent, the users thought it completely unintuitive. Longtime D users might remember when I was the lone holdout on how the name lookup worked in D. I thought it was perfectly straightforward, and it was easy to implement in clean code. Nobody agreed with me. So now we have a complicated 2-phase lookup that everybody likes. Go figure! P.S. C and C++ require semantic analysis in order to parse the source code correctly (the C cast expressions, and the >> tokens in C++). These are unnecessary and simply bad language design.
Mar 23
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 24/03/2026 12:48 PM, Walter Bright wrote:
 But in defense, in the 1980s I attempted to develop the be-all and end- 
 all windowing user interface library.
Did you ever study IBM CUA?
Mar 23
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/23/2026 4:49 PM, Richard (Rikki) Andrew Cattermole wrote:
 Did you ever study IBM CUA?
I had the book but never read it. It came out years after my attempt.
Mar 23
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 24/03/2026 3:55 PM, Walter Bright wrote:
 On 3/23/2026 4:49 PM, Richard (Rikki) Andrew Cattermole wrote:
 Did you ever study IBM CUA?
I had the book but never read it. It came out years after my attempt.
I've got it as well, the last of the series I think it is. https://www.amazon.com/Object-Oriented-Interface-Design-Common-Guidelines/dp/1565291700 You should open it up, it talks about how it combines earlier CUA specifications ;) It originally was about TUI's, not even graphical.
Mar 23
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/23/2026 8:01 PM, Richard (Rikki) Andrew Cattermole wrote:
 On 24/03/2026 3:55 PM, Walter Bright wrote:
 On 3/23/2026 4:49 PM, Richard (Rikki) Andrew Cattermole wrote:
 Did you ever study IBM CUA?
I had the book but never read it. It came out years after my attempt.
I've got it as well, the last of the series I think it is. https://www.amazon.com/Object-Oriented-Interface-Design-Common-Guidelines/dp/1565291700 You should open it up, it talks about how it combines earlier CUA specifications ;) It originally was about TUI's, not even graphical.
I sold the book (!) because I wanted to focus on other things and the book fetched a good price.
Mar 24
parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 24/03/2026 8:05 PM, Walter Bright wrote:
 On 3/23/2026 8:01 PM, Richard (Rikki) Andrew Cattermole wrote:
 On 24/03/2026 3:55 PM, Walter Bright wrote:
 On 3/23/2026 4:49 PM, Richard (Rikki) Andrew Cattermole wrote:
 Did you ever study IBM CUA?
I had the book but never read it. It came out years after my attempt.
I've got it as well, the last of the series I think it is. https://www.amazon.com/Object-Oriented-Interface-Design-Common- Guidelines/dp/1565291700 You should open it up, it talks about how it combines earlier CUA specifications ;) It originally was about TUI's, not even graphical.
I sold the book (!) because I wanted to focus on other things and the book fetched a good price.
Oh lol, its cheap now and also on archive.org.
Mar 24
prev sibling parent reply Max Samukha <maxsamukha gmail.com> writes:
On Monday, 23 March 2026 at 23:48:27 UTC, Walter Bright wrote:

 So now we have a complicated 2-phase lookup that everybody 
 likes.
Again, not everybody likes it.
Mar 24
parent reply user1234 <user1234 12.de> writes:
On Tuesday, 24 March 2026 at 10:36:19 UTC, Max Samukha wrote:
 On Monday, 23 March 2026 at 23:48:27 UTC, Walter Bright wrote:

 So now we have a complicated 2-phase lookup that everybody 
 likes.
Again, not everybody likes it.
I don't know what this "2-phases lookup system" is refering to (my interest in D started by 2014 let's say and I have the feeling that this is an older topic). What was the problem in first place ?
Mar 24
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Tue, Mar 24, 2026 at 12:38:28PM +0000, user1234 via Digitalmars-d wrote:
 On Tuesday, 24 March 2026 at 10:36:19 UTC, Max Samukha wrote:
 On Monday, 23 March 2026 at 23:48:27 UTC, Walter Bright wrote:
 
 So now we have a complicated 2-phase lookup that everybody likes.
Again, not everybody likes it.
I don't know what this "2-phases lookup system" is refering to (my interest in D started by 2014 let's say and I have the feeling that this is an older topic). What was the problem in first place ?
In the beginning, D's symbol lookup mechanism was very simple and straightforward: whenever an import statement is encountered, symbols from an imported module are loaded and injected into the symbol table at the current scope. This is straightforward to implement (you literally translate "import xyz" as parsing xyz and adding its public symbols to the current scope's symbol table). However, that led to counterintuitive behaviours like: void main() { myFunc("abc"); } void myFunc(string text) { writeln(text); // prints "abc" import std.conv; writeln(text); // prints "" } Walter was very resistant to changing this behaviour, as all proposed solutions involved "inelegant" complications in the semantics and implementation of "import". Eventually, however, the crowd prevailed and Walter relented. T -- Why does a pound of hamburger have less energy than a pound of steak? Because it is in the ground state.
Mar 24
next sibling parent reply user1234 <user1234 12.de> writes:
On Tuesday, 24 March 2026 at 14:36:57 UTC, H. S. Teoh wrote:
 On Tue, Mar 24, 2026 at 12:38:28PM +0000, user1234 via 
 Digitalmars-d wrote:
 On Tuesday, 24 March 2026 at 10:36:19 UTC, Max Samukha wrote:
 On Monday, 23 March 2026 at 23:48:27 UTC, Walter Bright 
 wrote:
 
 So now we have a complicated 2-phase lookup that everybody 
 likes.
Again, not everybody likes it.
I don't know what this "2-phases lookup system" is refering to (my interest in D started by 2014 let's say and I have the feeling that this is an older topic). What was the problem in first place ?
In the beginning, D's symbol lookup mechanism was very simple and straightforward: whenever an import statement is encountered, symbols from an imported module are loaded and injected into the symbol table at the current scope. This is straightforward to implement (you literally translate "import xyz" as parsing xyz and adding its public symbols to the current scope's symbol table). However, that led to counterintuitive behaviours like: void main() { myFunc("abc"); } void myFunc(string text) { writeln(text); // prints "abc" import std.conv; writeln(text); // prints "" } Walter was very resistant to changing this behaviour, as all proposed solutions involved "inelegant" complications in the semantics and implementation of "import". Eventually, however, the crowd prevailed and Walter relented. T
Thanks. I would not like to be rude and reopen old wounds but this looks more a problem of `text` being either a variable or a function and as D allows function calls without params to me it looks like it has never been a problem of lookup. Now imagine std.conv.text is a global variable. That does not change anything. Order of declaration matters in the local scope. Anyway, despite of this curious change, the front-end is still fast.
Mar 24
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Tue, Mar 24, 2026 at 03:25:29PM +0000, user1234 via Digitalmars-d wrote:
 On Tuesday, 24 March 2026 at 14:36:57 UTC, H. S. Teoh wrote:
[...]
 In the beginning, D's symbol lookup mechanism was very simple and
 straightforward: whenever an import statement is encountered,
 symbols from an imported module are loaded and injected into the
 symbol table at the current scope.  This is straightforward to
 implement (you literally translate "import xyz" as parsing xyz and
 adding its public symbols to the current scope's symbol table).
 
 However, that led to counterintuitive behaviours like:
 
 	void main() {
 		myFunc("abc");
 	}
 	void myFunc(string text) {
 		writeln(text);	// prints "abc"
 		import std.conv;
 		writeln(text);	// prints ""
 	}
[...]
 I would not like to be rude and reopen old wounds but this looks more
 a problem of `text` being either a variable or a function and as D
 allows function calls without params to me it looks like it has never
 been a problem of lookup. Now imagine std.conv.text is a global
 variable. That does not change anything. Order of declaration matters
 in the local scope.
It's not about `text` being a function called without parens; it's about an imported module hijacking symbols in the local scope. The same problem occurs in this situation: ``` // xyz.d module xyz; static immutable text = "haha you got hijacked"; // main.d module main; void main() { myFunc("abc"); } void myFunc(string text) { writeln(text); // prints "abc" import xyz; // N.B.: we never imported `text` explicitly writeln(text); // prints "haha you got hijacked" } ``` This is a problem because perhaps module xyz didn't declare `text` when this code was written. Then later on, upstream rewrote the implementation and added a declaration of `text`. Suddenly, unrelated code in myFunc() has broken because its semantics silently changed from under its carpet: the newly-added declaration of `text` in xyz has hijacked the function parameter `text` in an unrelated module that just happened to import it. This is only one instance of the problem. Another instance concerns *private* symbols in the imported module causing a conflict with a local symbol in the importing module. (Remember, this is a logical consequence of the simple and elegant concept that `import` simply injects symbols into the local scope -- the injected symbols include private symbols. Treating private symbols specially was one of the "inelegant" things that complicated the implementation of imports, among other things.) T -- My stomach is flat. The L is just silent!
Mar 24
parent reply user1234 <user1234 12.de> writes:
On Tuesday, 24 March 2026 at 15:41:59 UTC, H. S. Teoh wrote:
 On Tue, Mar 24, 2026 at 03:25:29PM +0000, user1234 via 
 Digitalmars-d wrote:
 [...]
[...]
 [...]
[...]
 [...]
It's not about `text` being a function called without parens; it's about an imported module hijacking symbols in the local scope. The same problem occurs in this situation: [...]
Sorry, I see it now. It's the old "furnisher" problem. There was something about that a few days ago on Y news https://www.boxyuwu.blog/posts/an-incoherent-rust/
Mar 24
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/24/2026 12:23 PM, user1234 wrote:
 https://www.boxyuwu.blog/posts/an-incoherent-rust/
If I understand this correctly, D has solved this problem: ```d import a; // exports 'x' import b; // exports 'x' int z = x; // error, multiple definition of 'x' ``` The resolution: ```d import a; // exports 'x' import b; // exports 'x' alias x = b.x; int z = x; // b.x is selected ```
Mar 24
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 3/25/26 03:32, Walter Bright wrote:
 On 3/24/2026 12:23 PM, user1234 wrote:
 https://www.boxyuwu.blog/posts/an-incoherent-rust/
If I understand this correctly, D has solved this problem: ```d import a;   // exports 'x' import b;   // exports 'x' int z = x; // error, multiple definition of 'x' ``` The resolution: ```d import a;   // exports 'x' import b;   // exports 'x' alias x = b.x; int z = x; // b.x is selected ```
Then so has Rust: ```rust mod a { pub const x : i32 = 2; } mod b { pub const x : i32 = 3; } use a::*; use b::*; const z : i32 = x; // error: `x` is ambiguous fn main(){ print!("{}", z); } ``` The resolution: ```rust mod a { pub const x : i32 = 2; } mod b { pub const x : i32 = 3; } use a::*; use b::*; use b::x as x; const z : i32 = x; // b::x is selected fn main(){ print!("{}", z); } // 3 ``` The issue is a bit more fundamental to their traits design than just disambiguating names. They don't even have names for implementations of traits. Once you have names, in order to detect mismatches between associated types, you need some sort of dependent typing, for example path-dependent types. This is because implementations are values, and the associated types depend on these values. Enforcing that there can be only one implementation of a trait for a given type gets around this dependency: you can treat the mapping from implementation to associated type as just a mapping from the type to the associated type given by the unique implementation.
Mar 26
parent Walter Bright <newshound2 digitalmars.com> writes:
It does look like the same idea in the example you gave.

D has a lot less typing!
Mar 27
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 3/24/26 15:36, H. S. Teoh wrote:
 However, that led to counterintuitive behaviours like:
 
 	void main() {
 		myFunc("abc");
 	}
 	void myFunc(string text) {
 		writeln(text);	// prints "abc"
 		import std.conv;
 		writeln(text);	// prints ""
 	}
 
 Walter was very resistant to changing this behaviour, as all proposed
 solutions involved "inelegant" complications in the semantics and
 implementation of "import".  Eventually, however, the crowd prevailed
 and Walter relented.
Not really, the problem is not fixed: ```d import std.stdio; string readAndLog(string filename){ import std.file; auto text=readText(filename); write(filename," read successfully!\n"); return text; } void main(){ writeln(readAndLog("important_data.txt")); } ``` https://github.com/dlang/dmd/issues/19272 So what we have now is a compromise: It's both broken and not entirely straightforward.
Mar 24
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Tue, Mar 24, 2026 at 08:50:26PM +0100, Timon Gehr via Digitalmars-d wrote:
[...]
 ```d
 import std.stdio;
 
 string readAndLog(string filename){
     import std.file;
     auto text=readText(filename);
     write(filename," read successfully!\n");
     return text;
 }
 
 void main(){
     writeln(readAndLog("important_data.txt"));
 }
 ```
 
 https://github.com/dlang/dmd/issues/19272
 
 So what we have now is a compromise: It's both broken and not entirely
 straightforward.
And the solution will add yet more complication to an already complex import symbol resolution system. :-D T -- What do you get if you throw a grenade into a French kitchen? Linoleum blown apart.
Mar 24
parent reply user1234 <user1234 12.de> writes:
On Tuesday, 24 March 2026 at 20:17:16 UTC, H. S. Teoh wrote:
 On Tue, Mar 24, 2026 at 08:50:26PM +0100, Timon Gehr via 
 Digitalmars-d wrote: [...]
 ```d
 import std.stdio;
 
 string readAndLog(string filename){
     import std.file;
     auto text=readText(filename);
     write(filename," read successfully!\n");
     return text;
 }
 
 void main(){
     writeln(readAndLog("important_data.txt"));
 }
 ```
 
 https://github.com/dlang/dmd/issues/19272
 
 So what we have now is a compromise: It's both broken and not 
 entirely straightforward.
And the solution will add yet more complication to an already complex import symbol resolution system. :-D T
git revert is so simple
Mar 24
parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Tue, Mar 24, 2026 at 11:31:25PM +0000, user1234 via Digitalmars-d wrote:
 On Tuesday, 24 March 2026 at 20:17:16 UTC, H. S. Teoh wrote:
 On Tue, Mar 24, 2026 at 08:50:26PM +0100, Timon Gehr via Digitalmars-d
 wrote: [...]
[...]
 https://github.com/dlang/dmd/issues/19272
 
 So what we have now is a compromise: It's both broken and not
 entirely straightforward.
And the solution will add yet more complication to an already complex import symbol resolution system. :-D
[...]
 git revert is so simple
Yes, and it will equally simply break a ton of user code that has come to rely on the new behaviour, thus causing outrage among users at the next release. :-P T -- In theory, there is no difference between theory and practice.
Mar 24
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
A shout out to Martin Nowak who implemented the 2 phase lookup.
Mar 24