Jump to page: 1 2 3
Thread overview
The point of const, scope, and other attributes
May 11, 2022
Walter Bright
May 11, 2022
Walter Bright
May 11, 2022
Dennis
May 11, 2022
Salih Dincer
May 11, 2022
Dukc
May 11, 2022
IGotD-
May 11, 2022
Walter Bright
May 13, 2022
Fry
May 13, 2022
H. S. Teoh
May 14, 2022
Tejas
May 14, 2022
Ali Çehreli
May 14, 2022
H. S. Teoh
May 11, 2022
Walter Bright
May 11, 2022
jmh530
May 11, 2022
Dennis
May 11, 2022
jmh530
May 11, 2022
Walter Bright
May 11, 2022
jmh530
May 12, 2022
Walter Bright
May 10, 2022
The original D did not have them.

Many people who have written D code without them, and then added them. Then they noticed that although those attributes are meant to prevent bugs, adding them to existing code did not find any bugs. So what is the point?

A small anecdote. Before the backend was recently converted to D, I was invited to try out one of the good C++ bug checkers. I thought great, this will find bugs in the backend.

While it generated about a thousand false positives, it didn't find a single bug.

But I realized that I was running the checker over code that had already been thoroughly debugged through a lot of usage. Of course it didn't find anything. All the memory corruption bugs, bad casts, etc., had already been squeezed out.

The point of the checker was to find bugs in fresh code, and find them before even trying to run the code. The checker saves you the trouble of finding those bugs yourself. It saves you the trouble caused by shipping those bugs.

---

Another story. Back when I was programming in C, and was inexperienced, my code had a lot of bugs in it. I made every mistake, over and over. Having a memory corruption bug was a depressingly routine problem, and they were always hard to debug. But, over time, and endless experience, I gradually learned to avoid writing those bugs. I can't even recall the last time I wrote a memory corruption bug. (Of course, saying this, tomorrow for sure I'll make that mistake!)

But this is really doing things the hard way. I'm lazy, I like the easy way.

---

There are other reasons for the attributes, such as making code more understandable. A `const` parameter tells you the function doesn't modify that argument, or what it points to. A `scope` parameter tells you the function doesn't save a copy of it, so the caller can confidently delete the data. And so on.
May 10, 2022
The C++ checker did gripe about some of the debug printf format strings not quite matching the arguments, such as printing a pointer with %x. The errors were benign, though. printf format mismatch was another situation where I wrote lots of bugs.

But that experience helped push me towards getting D to do printf format checking. The checking doesn't find bugs anymore in existing code, because those errors never make it through a compilation. It also trained me to not make those mistakes anymore.

So is that useful? Hell yes. I don't even have to bother checking the formats anymore when reviewing code. Neither need anyone else. Ain't that the shiznit? Why didn't we do that eons ago? Sigh.
May 11, 2022
On Wednesday, 11 May 2022 at 05:02:31 UTC, Walter Bright wrote:
> There are other reasons for the attributes, such as making code more understandable. A `const` parameter tells you the function doesn't modify that argument, or what it points to. A `scope` parameter tells you the function doesn't save a copy of it, so the caller can confidently delete the data. And so on.

Very satisfying information that I have encountered for the first time on subjects that I have never used, thank you. Maybe I'll use it more often now :)

SDB@79
May 11, 2022

On Wednesday, 11 May 2022 at 05:02:31 UTC, Walter Bright wrote:

>

[snip]

I am increasingly of the opinion that DIP1000 is too complicated for the average programmer to understand well. I find myself often stratching my head with it, it would be overwhelming for a beginner.

Still, I do not advocate going back to pre-DIP1000 semantics because those are simply unsafe when it comes to slicing static arrays. I think we should start teaching two rules:

1: Never, ever brute force your way through DIP1000 errors with @trusted because you do not understand return, scope and ref.
2: If you're having trouble, GC-allocate the variables you're trying to use.

The nice thing about DIP1000 is that one can't accidently introduce bugs as long as s/he follows rule 1.

Regarding other attributes, I think it's good that our const/immutable/shared are transitive. Otherwise the attribute soup problem we have would be even worse.

May 11, 2022
On Wednesday, 11 May 2022 at 05:15:00 UTC, Walter Bright wrote:
> The checking doesn't find bugs anymore in existing code, because those errors never make it through a compilation.

Never say never ;)

https://github.com/dlang/dmd/pull/13987
https://issues.dlang.org/show_bug.cgi?id=23020

May 11, 2022

On Wednesday, 11 May 2022 at 07:21:02 UTC, Dukc wrote:

>

I am increasingly of the opinion that DIP1000 is too complicated for the average programmer to understand well. I find myself often stratching my head with it, it would be overwhelming for a beginner.

C# went the other way, no const parameters at all. Maybe because it doesn't make much sense because there is a lot of runtime violations of const underneath. Anyway, I like the C# aproach better because it is simpler for the programmer.

Another problem is that all this badging tend to get out of hand and it breeds more badges until it doesn't mean anything. For example what will happen if D adds a mutable attribute?

May 11, 2022
On Wednesday, 11 May 2022 at 05:02:31 UTC, Walter Bright wrote:
> The original D did not have them.
>
> [snip]

I'm generally pro-attribute, but I can't help but find myself scratching my head a bit with when to use return ref scope. And then for whatever reason there was a change so that now scope return has to be return scope.

So I think it might help to lay out some principles vis a vis attributes.

The first principle I would emphasize is "time to first plot". I learned about this from the Julia language, where they are talking about actual time it takes for Julia to boot up and be able to plot something. However, I think you can think about it a bit more metaphorically as "time for a new user to do something." This principle implies that D's opt-in approach to attributes is good since a new user can start doing work without needing to worry about them at first. On the basis of this principle, moving toward @safe by default or const/immutable by default would be bad.

The second principle is harder to describe, but it is related to the relationship between complexity, levels of abstraction, and mental burden. In the back of my head, I am recalling a chart that compared C++ and D with D listed as less complex than C++ (I don't recall what the other axis was, maybe something like expressive power). Anyway, if a programming language is considering adding a feature and there is a less complex version that abstracts away from some details versus a more complex version that operates on a higher level of abstraction, then it can be the case that the more complex version operating on a higher level of abstraction might actually have a lower mental burden. Consider the treatment of lifetimes in DIP 1000 vs lifetimes in Rust. DIP 1000's use of return probably covers most peoples' use cases, but might also require a higher mental burden to figure out exactly what it means since it is less abstract. By contrast, Rust lifetimes are implemented in a more complex way, but also operating at a higher level of abstraction. The higher level of abstraction might also result in a lower mental burden. Long story short, sometimes adding a more complex feature can be a good thing if it comes associated with a higher level of abstraction.

Of course, the trade off between the complexity and the ability to generate improved performance/safety is another important principle.
May 11, 2022

On Wednesday, 11 May 2022 at 12:54:31 UTC, jmh530 wrote:

>

By contrast, Rust lifetimes are implemented in a more complex way, but also operating at a higher level of abstraction. The higher level of abstraction might also result in a lower mental burden.

I don't know what you mean with 'higher level of abstraction' here, can you give an example?

May 11, 2022

On Wednesday, 11 May 2022 at 13:56:32 UTC, Dennis wrote:

>

On Wednesday, 11 May 2022 at 12:54:31 UTC, jmh530 wrote:

>

By contrast, Rust lifetimes are implemented in a more complex way, but also operating at a higher level of abstraction. The higher level of abstraction might also result in a lower mental burden.

I don't know what you mean with 'higher level of abstraction' here, can you give an example?

I don't know if I'm using it in the computer science way...

I mean it like how if you look at the rules of arithmetic (distributive, associative, etc.) these apply to what we normally think of as numbers (integers, reals, etc.) but then they apply different to other things (like matrices). You can abstract from these ideas to group theory and rings and make statements about all algebras with particular rules.

So with respect to lifetimes, in D we can apply the return attribute to a function parameter. From Rust's perspective, this is equivalent to an explicit lifetime for a function parameter that lasts as long as the return. I was phrasing it as the Rust approach is a higher level of of abstraction, but you could also think of it as the D approach is a subset of the Rust approach. I think this was done on purpose, acknowledging that the return annotation is the most often way Rust lifetimes are used. My response would be that is all well and fine, but I still find this confusing:
https://dlang.org/spec/function.html#ref-return-scope-parameters

May 11, 2022
On 5/11/2022 12:21 AM, Dukc wrote:
> Regarding other attributes, I think it's good that our const/immutable/shared are transitive. Otherwise the attribute soup problem we have would be even worse.

My experience with non-transitive const in C and C++ is it is more "documentation" than anything reliable. For example, if you're handing a reference to a non-trivial data structure to a function, there's no way to say "just read the data structure, don't change it". People schlepp a `const` on it anyway, but it doesn't work.
« First   ‹ Prev
1 2 3