Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Sunday, 6 February 2022 at 15:43:39 UTC, Ola Fosheim Grøstad wrote: 3. *The politics of language improvements*: I don't think this should be a library type. I think this feature is too important for that. To me this smells of let's move the syntax to a library to avoid any discussion about breaking changes. Design considerations should not become political, we need to get rid of politics and focus on principled strategies that makes the whole eco system attractive to more developers (the ones we don't have). This is a disrespectful comment. You're implying that your opinion is rational and apolitical, disagreeing with it is irrational politics. It is true that no decisions are fully politics-free, but please don't pretend that you are above all others here in that regard.
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Monday, 7 February 2022 at 19:57:28 UTC, forkit wrote: First, I'm not 'insisting' on anything. I'm just expressing a view. nodiscard is already used by more programmers that D is likely to ever adopt. Indeed, it's these programmers that D is trying to adopt. I'm not sure forcing such people to adapt is necessarily the right approach. I'll have to side with forkit there. In audio software (at the very least) you pay very dearly the price of introducing new _words_, because the existing words embodies existing meaning and practice that users have internalized. If you call your "Amount" parameter "Dry/wet" instead, then misunderstandings will happen for years without ever a chance to correct them, because "Dry/wet" means something already in the users mind. If you call your mutex a "synchronizer" then you have to tell everyone what is a synchronizer, why it is needed as a concept, and so on. Think: "slices" and "dynamic arrays" in D, a distinction that is of no use and begets a lot of misunderstandings. Hence some does of of reusing, reduce mental load. Paucity of words helps the cast of D explainers.
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Wednesday, 9 February 2022 at 14:30:30 UTC, Guillaume Piolat wrote: Hence some does of of reusing, some dose of reuse*
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Wednesday, 9 February 2022 at 14:30:30 UTC, Guillaume Piolat wrote: On Monday, 7 February 2022 at 19:57:28 UTC, forkit wrote: First, I'm not 'insisting' on anything. I'm just expressing a view. nodiscard is already used by more programmers that D is likely to ever adopt. Indeed, it's these programmers that D is trying to adopt. I'm not sure forcing such people to adapt is necessarily the right approach. I'll have to side with forkit there. In audio software (at the very least) you pay very dearly the price of introducing new _words_, because the existing words embodies existing meaning and practice that users have internalized. In D, there is no existing word for this, so from that perspective both "mustuse" and "nodiscard" are equally valid. In other languages, there are multiple existing words: - C++17: [[nodiscard]] - C (gcc/clang): __attribute__((warn_unused_result)) - Rust: #[must_use] If you are used to C++, then you will find "nodiscard" natural, and "mustuse" will require you to learn a new word. If you are used to Rust, then you will find "mustuse" natural, and "nodiscard" would have required you to learn a new word. And, of course, if you are used to Python or Javascript or one of the many other languages that has no word for this at all, it will make little difference to you either way. C++ is quite popular, but it is not the only popular language, and there are many D programmers who have never used C++ at all, let alone C++17 or later. Therefore, it is a mistake to assume that all or even most D programmers have already internalized "nodiscard", or indeed *any* particular word for this concept.
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On 10/02/2022 5:21 AM, Paul Backus wrote: - C (gcc/clang): __attribute__((warn_unused_result)) C23 will also have [[nodiscard]] Not only will it have that on functions, but also support a string too. Unfortunately its looking like we have chosen to diverge from C, and therefore won't be completely C compatible soon. Will be exciting as to what kind of bugs crop up because of this!
Re: Added copy constructors to "Programming in D"
On 2/9/22 02:15, Anonymouse wrote: On Saturday, 8 January 2022 at 02:07:10 UTC, Ali Çehreli wrote: 2) The other noteworthy change in the book is my now-different stance on variables: Now I recommend 'const' over 'immutable' for variables. I'm curious, could you elaborate a bit on this? I skimmed through the page on Immutability but I didn't find anything explaining it. To make sure we are looking at the same version, the new content has the following section: in parameters As we will see in the next chapter, in implies const and is more useful with the ‑preview=in command line switch. For that reason, I recommend in parameters over const parameters. http://ddili.org/ders/d.en/const_and_immutable.html In short, in the past, under the influence of "stronger guarantee" clearly sounding better, I was recommending immutable for variables: immutable i = 42; Realizing that I don't do that in my own code, now I recommend const: const i = 42; Further, now that we have -preview=in (thanks to Mathias Lang), I recommend 'in' over const for parameters. (Actually, I had always recommended 'in' and did use it in some of the examples in the book (inconsistently) but I wasn't using it in my own code.) But the whole thing is complicated. :) So, I asked for and received opinions on this thread: https://forum.dlang.org/thread/sig2d4$657$1...@digitalmars.com I don't find the common description helpful: "immutable provides stronger guarantee." That's true and sounds better than mere "strong" but it does not help a programmer with deciding on which one to use. Rather, I like the following distinction: - const is a promise - immutable is a requirement Now, that helps me decide which one to use when. Let's start with a function: void foo(in A a, immutable B b) { // ... } There, foo "promises" to not mutate 'a' ('in' implies 'const') and "requires" that 'b' is not mutated by any other code. With that understanding, it is silly to "require" that no other code mutates a local variable: void x() { immutable b = B(); // Really? Who can mutate it? // ... } So, the following is logical (and shorter :) ): void x() { const b = B(); // ... } Now, if I need to pass it to a function that "requires" immutable, of course I will have to define it as immutable to satisfy that requirement: void x() { immutable b = B(); foo(A(), b); // ... } Aside: One of the confusions is that 'const' would work there as well *if* B did not have any indirection. But if it's defined as e.g. struct B { int[] arr; } then, x() will not compile with 'const b'. In summary: Variables: 'const' by default, 'immutable' as needed (because somebody requires it) Parameters: 'in' by default, 'immutable' if required That's my view. I think there are others who disagree with parts of it and that's why this issue is surprisingly complicated. Ali
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Wednesday, 9 February 2022 at 16:21:24 UTC, Paul Backus wrote: [snip] In D, there is no existing word for this, so from that perspective both "mustuse" and "nodiscard" are equally valid. In other languages, there are multiple existing words: - C++17: [[nodiscard]] - C (gcc/clang): __attribute__((warn_unused_result)) - Rust: #[must_use] If you are used to C++, then you will find "nodiscard" natural, and "mustuse" will require you to learn a new word. If you are used to Rust, then you will find "mustuse" natural, and "nodiscard" would have required you to learn a new word. But what color should the bike shed be? ;)
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Wednesday, 9 February 2022 at 16:21:24 UTC, Paul Backus wrote: C++ is quite popular, but it is not the only popular language, and there are many D programmers who have never used C++ at all, let alone C++17 or later. Therefore, it is a mistake to assume that all or even most D programmers have already internalized "nodiscard", or indeed *any* particular word for this concept. There is also the Nim "discard" statement. I wonder why noone stole that feature. Feels more natural to me speak about the error being avoided, than a vague statement like "must use". C++17 is the most used language in the native space, so yes I think it takes precedence in the minds of native programmers. But, it is a level of bikeshedding I'm not keen on entering.
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Wednesday, 9 February 2022 at 16:21:24 UTC, Paul Backus wrote: On Wednesday, 9 February 2022 at 14:30:30 UTC, Guillaume Piolat wrote: On Monday, 7 February 2022 at 19:57:28 UTC, forkit wrote: First, I'm not 'insisting' on anything. I'm just expressing a view. nodiscard is already used by more programmers that D is likely to ever adopt. Indeed, it's these programmers that D is trying to adopt. I'm not sure forcing such people to adapt is necessarily the right approach. I'll have to side with forkit there. In audio software (at the very least) you pay very dearly the price of introducing new _words_, because the existing words embodies existing meaning and practice that users have internalized. In D, there is no existing word for this, so from that perspective both "mustuse" and "nodiscard" are equally valid. In other languages, there are multiple existing words: - C++17: [[nodiscard]] - C (gcc/clang): __attribute__((warn_unused_result)) - Rust: #[must_use] C++17: [[nodiscard]] is just a warning; must use another compiler flag in order to have same @mustuse in D -> already discrepancy in meaning
Re: Added copy constructors to "Programming in D"
On Wed, Feb 09, 2022 at 10:28:15AM -0800, Ali Çehreli via Digitalmars-d-announce wrote: [...] > - const is a promise > > - immutable is a requirement [...] Strictly speaking, that's not really an accurate description. :-P A more accurate description would be: - const: I cannot modify the data (but someone else might). - immutable: I cannot modify the data, AND nobody else can either. The best way I've found to understand the relationship between const, immutable, and mutable in D is the following "type inheritance" diagram (analogous to a class inheritance diagram): const / \ (mutable) immutable Const behaves like the "base class" (well, base type, sortof) that either mutable or immutable can implicitly convert to. Mutable and immutable, however, are mutually incompatible "derived classes" that will not implicitly convert to each other. (Of course, the reality is somewhat more complex than this, but this is a good, simple conceptual starting point to understanding D's type system.) Const means whoever holds the reference to the data cannot modify it. So it's safe to hand them both mutable and immutable data. Mutable means you are allowed to modify it, so obviously it's illegal to pass in const or immutable. Passing mutable to const is OK because the recipient cannot modify it, even though the caller himself may (since he holds a mutable reference to it). Immutable means NOBODY can modify it, not even the caller. I.e., *nobody* holds a mutable reference to the data. So you cannot pass mutable to immutable. Obviously, it's safe to pass immutable to const (the callee cannot modify it anyway, so we're OK). But you cannot pass const to immutable, because, as stated above, a const reference might be pointing to mutable data: even though the holder of the reference cannot himself modify it, it may have come from a mutable reference somewhere else. Allowing it would break the rule that immutable means *nobody* has a mutable reference to the data. So that's not allowed. IOW, "downcasting" in the above "type hierarchy" is not allowed, in general. However, there's a special case where mutable does implicitly convert to mutable: this is if the mutable reference is unique, meaning that it's the only reference that exists to that data. In such a case, it's OK to convert that mutable reference to an immutable one, provided the mutable reference immediately goes out of scope. After that point, nobody holds a mutable reference to it anymore, so it fits into the definition of immutable. This happens when a mutable reference is returned from a pure function: MyData createData() pure { MyData result; // N.B.: mutable return result; // mutable reference goes out of scope } // OK: function is pure and reference to data is unique immutable MyData data = createData(); The `pure` ensures that createData didn't cheat and store a mutable reference to the data in some global variable, so the reference to MyData that it returns is truly unique. So in this case we allow mutable to implicitly convert to immutable. There's also another situation where immutable is allowed to implicitly convert to mutable: this is when the data is a by-value type containing no indirections. Essentially, we're making a copy of the immutable data, so it doesn't matter if we modify the copy, since we're not actually modifying the original data. // Now, w.r.t. the original question of when we should use const vs. immutable: - For local variables, there's no practical difference between const and immutable, because by definition the current function holds the only reference to it, so there can't be any mutable reference to it. I would just use immutable in this case -- the compiler may be able to optimize the code better knowing that there can't be any mutable reference anywhere else (though in theory the compiler should have already figured this out, since it's a local variable). - For function parameters, I would always use const over immutable, unless there was a reason I want to guarantee that nobody else holds a mutable reference to that argument (e.g., I'm storing the reference in a data structure that requires the data not to be mutated afterwards). Using const makes the function usable with both mutable and immutable arguments, which is more flexible when you don't need to guarantee that the data will never be changed by anybody. Some people may prefer `in` instead of const for function parameters: it's more self-documenting, and if you use -preview=in, it means `const scope`, which adds an additional check that you don't accidentally leak reference to parameters past the scope of the function. T -- Windows 95 was a joke, and Windows 98 was the punchline.
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Wednesday, 9 February 2022 at 17:40:31 UTC, jmh530 wrote: On Wednesday, 9 February 2022 at 16:21:24 UTC, Paul Backus wrote: [snip] In D, there is no existing word for this, so from that perspective both "mustuse" and "nodiscard" are equally valid. In other languages, there are multiple existing words: - C++17: [[nodiscard]] - C (gcc/clang): __attribute__((warn_unused_result)) - Rust: #[must_use] If you are used to C++, then you will find "nodiscard" natural, and "mustuse" will require you to learn a new word. If you are used to Rust, then you will find "mustuse" natural, and "nodiscard" would have required you to learn a new word. But what color should the bike shed be? ;) Color is a perceptual experience, and its impact on psychological functioning has been well researched.
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Wednesday, 9 February 2022 at 17:48:29 UTC, Guillaume Piolat wrote: There is also the Nim "discard" statement. Just change the default to not allowing return values to be discarded. When you really want to, do: ``` cast(void) function_with_return_value(…) ``` Or something like that.
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Wednesday, 9 February 2022 at 17:54:17 UTC, rikki cattermole wrote: On 10/02/2022 5:21 AM, Paul Backus wrote: - C (gcc/clang): __attribute__((warn_unused_result)) C23 will also have [[nodiscard]] Not only will it have that on functions, but also support a string too. Unfortunately its looking like we have chosen to diverge from C, and therefore won't be completely C compatible soon. Will be exciting as to what kind of bugs crop up because of this! Exactly. So now C++ and C will have nodiscard, and when those developers come over to D (the developers most targetted by D btw), someone will have to explain why D decided on a different color for the bikeshed.
Re: Added copy constructors to "Programming in D"
On Saturday, 8 January 2022 at 02:07:10 UTC, Ali Çehreli wrote: 2) The other noteworthy change in the book is my now-different stance on variables: Now I recommend 'const' over 'immutable' for variables. I'm curious, could you elaborate a bit on this? I skimmed through the page on Immutability but I didn't find anything explaining it.
Re: DIP 1038--"@mustUse" (formerly "@noDiscard")--Accepted
On Wednesday, 9 February 2022 at 10:59:03 UTC, Dukc wrote: You're implying that your opinion is rational and apolitical, disagreeing with it is irrational politics. I am implying that there are many symptoms of people not being willing to champion the best possible design and instead have started to look for what they think is easy to get through. I see that in this DIP, in other DIPs and in comments about DIPs people are contemplating. The accumulated outcome of such political design processes are usually not great. I will later try to create a separate thread for this, as Paul does not want this topic in this thread.
Re: Added copy constructors to "Programming in D"
Why do we even bother with `in` when we can do: alias In(T) = const scope T; void test(In!int n) { pragma(msg, typeof(n)); } ? onlineapp.d(3): Deprecation: storage class `scope` has no effect in type aliases const(int) ...oh
Re: Added copy constructors to "Programming in D"
On 2/9/22 18:11, Meta wrote: > Why do we even bother with `in` when we can do: > > alias In(T) = const scope T; > > void test(In!int n) { > pragma(msg, typeof(n)); > } > > ? > > onlineapp.d(3): Deprecation: storage class `scope` has no effect in type > aliases > const(int) > > ...oh I didn't know that but 'in' is underrated. There is heavy mental load on deciding parameter types: // Silly const: void foo(const(int)); // Too much information to the user (why // do they need to know that I will mutate the parameter): void foo(int); // When I know that copying is expensive // (which excludes rvalues; oops): void foo(ref const(ExpensiveToCopy)); // When I know that the type is non-copyable, // I have to use 'ref': void foo(ref const(NonCopyable)); What if foo is a template? ref or const or by-value? Or inout? Always or sometimes? Enough already! :) All I want to say is "I want to use this parameter as input." I don't care if its rvalue or expensive to copy or impossible to copy. I will define it as 'ref' if that's what I want but I shouldn't be thinking about any of the above for function inputs. I am happy to raise awareness of the new 'in': https://dlang.org/spec/function.html#in-params 'in' allows passing rvalues by ref! 'in' eliminates unwanted side-effects just because a function wants to use an object. 'in' passes non-copyable types by reference. Wow! That's engineering to my ears. :) Having said that, there is one thing that bothers me with 'in' or 'const'. Let's assume I want to mutate a copy of the parameter: void foo(in int i) { ++i;// ERROR } So I must make a copy: auto j = i; ++j;// ERROR Because 'auto' is too safe and takes 'const'. One more try: int j = i; ++j; Or perhaps in some generic code: import std.traits : Unqual; Unqual!(typeof(i)) j = i; ++j; Ok, fine. One more thing remains: Although 'i' may be the most logical name for the parameter, I cannot name 'j' as 'i' so I can mangle the parameter name just to prevent using 'i' in the function by accident: void foo(in int i_); That's not good because it changes what my callers see of my function. I can use 'i_' in the body (instead of 'j') but then I am open to the same mistake of using 'i' instead of 'i_' in the body. (Obviously not when mutating but when actually using.) Yeah, that issue bugs me a little. Ali