Re: Using const to Enforce Design Decisions
On Monday, 25 March 2019 at 13:00:07 UTC, Mike Parker wrote: Marco de Wild has penned a tutorial on D's const for the D blog. He gives an overview of the feature, how it differs from C++, and shows how he employed it in his mahjong game. The blog: https://dlang.org/blog/2019/03/25/using-const-to-enforce-design-decisions/ Reddit: https://www.reddit.com/r/programming/comments/b5a9jm/using_const_in_d_to_enforce_design_decisions/ I guess obligatory http://jmdavisprog.com/articles/why-const-sucks.html Would be nice to see the source code for this mahjong game as well.
Re: DIP 1016--ref T accepts r-values--Formal Assessment
On Thursday, 31 January 2019 at 22:00:10 UTC, Walter Bright wrote: On 1/31/2019 1:46 PM, Andrei Alexandrescu wrote: The proposal could actually disallow rvalues that have lvalue syntax, such as "symbol", "symbol[expr]", "symbol.symbol", "symbol.symbol[expr]", etc. Ugh. Gets hairy quickly. That's why it's problematic to have a rule that rvalues can be implicitly converted, but not lvalues. There's not a hard line between lvalues and rvalues. For example, foreach(i; 0..2) { int[] a = [1, 2]; assert(a[0] == 1]); a[0] = 3; // will this cause the assert to fail? } Why would it cause the assert to fail? A new array is constructed each loop.
Re: DIP 1016 should use expression lowering, not statement lowering
On Tuesday, 29 January 2019 at 15:44:02 UTC, Nicholas Wilson wrote: On Tuesday, 29 January 2019 at 11:52:40 UTC, Andrei Alexandrescu wrote: While writing this example: int[] a = cast(int[]) alloc.allocate(100 * int.sizeof); if (alloc.reallocate(a, 200 * int.sizeof)) { assert(a.length == 200); } => int[] a = cast(int[]) alloc.allocate(100 * int.sizeof); void[] __temp0 = a; if (alloc.reallocate(__temp0, 200 * int.sizeof) { assert(a.length == 200); } I noticed a problem - the lowering as informally described in DIP 1016 makes it difficult to figure how function calls present in control statements like if, while, etc. should behave. Where should the temporary go? An expression-based lowering clarifies everything. A statement-based lowering would need to work on a case basis for all statements involving expressions. On the contrary, an expression lowering cannot inject temporary declarations and is impossible. The correct lowering in the case for `if` & friends follows the form of C++ initialiser conditions(?) i.e: if (auto val = expr(); val) { ... }, or the slightly more ugly valid D: if ((){return expr(); }()) { ... } this lambdification will work for just about anything: if, while, assert... If it a condition then you can do the following in C++: if(int* val = expr()) { // use val, not nullptr } Where it is useful is in the following case: if(int val = expr(); val != -1) { } D follows C++'s construct for initializing a variable in control blocks. The only exception I think is for switch. switch(int val = expr()) // ok in C++, not ok in D { }
Re: DIP 1016--ref T accepts r-values--Formal Assessment
On Saturday, 26 January 2019 at 06:15:22 UTC, Walter Bright wrote: On 1/25/2019 7:44 PM, Manu wrote: I never said anything about 'rvalue references', The DIP mentions them several times in the "forum threads" section. I see you want to distinguish the DIP from that; I recommend a section clearing that up. However, my points about the serious problems with @disable syntax remain. A section comparing with the C++ solution is necessary as well, more than the one sentence dismissal. For example, how C++ deals with the: void foo(const int x); void foo(const int& x); situation needs to be understood and compared. Failing to understand it can lead to serious oversights. For example, C++ doesn't require an @disable syntax to make it work. [...] Should `s` be promoted to an int temporary, then pass the temporary by reference? I can find no guidance in the DIP. What if `s` is a uint (i.e. the implicit conversion is a type paint and involves no temporary)? As per the DIP; yes, that is the point. The text you seek is written: "[...]. The user should not experience edge cases, or differences in functionality when calling fun(int x) vs fun(ref int x)." I don't see how that addresses implicit type conversion at all. Anything that could be implicitly converted to use foo(int) can be implicitly converted to pass a ref to the temporary that was implicitly converted to int into foo(ref int). No rules change in this regard. If you don't see how this address type conversion perhaps a code sample might help? The one that was given with short: void foo(ref int); void bar(int); bar( short(10) ); // is ok foo( short(10) ); // expected to be ok short->int ; ref to temp passed to foo Just as bar(int) can be passed a short(10), foo(ref int) can be passed a reference to the temporary that was created as well. Don't accept naked ref unless you want these semantics. There is a suite of tools offered to use where this behaviour is undesirable. Naked `ref` doesn't do anything particularly interesting in the language today that's not *identical* semantically to using a pointer and adding a single '&' character at the callsite. It's not good enough. The DIP needs to specifically address what happens with implicit conversions. The reader should not be left wondering about what is implied. I often read a spec and think yeah, yeah, of course it must be that way. But it is spelled out in the spec, and reading it gives me confidence that I'm understanding the semantics, and it gives me confidence that whoever wrote the spec understood it. (Of course, writing out the implications sometimes causes the writer to realize he didn't actually understand it at all.) Furthermore, D has these match levels: 1. exact 2. const 3. conversion 4. no match If there are two or more matches at the same level, the decision is made based on partial ordering. How does adding the new ref/value overloading fit into that? The DIP goes over this, though not in a lot of detail. All the same rules apply as with the current implementation. Where there would be a compiler error trying to pass an rvalue would instead forward the value. Effectively what is being implemented is the following (for type matching only): void foo( ref int ); void foo( int value ) { foo( value ); } Anything that would have been passed to foo(int) is passed to foo(ref int) as a reference to a temporary instead. No rules are changed in this regard for matching, all the same rules apply (as stated in the DIP). It's pretty clear, unless you can give a specific problem faced where this doesn't hold? D is pretty strict to ensure rvalues aren't passed to ref's and that's what makes this relatively simple to implement without changing matching rules.
Re: DIP 1016--ref T accepts r-values--Formal Assessment
On Friday, 25 January 2019 at 11:56:58 UTC, Walter Bright wrote: On 1/24/2019 11:53 PM, Nicholas Wilson wrote: That the conflation of pass by reference to avoid copying and mutation is not only deliberate but also mitigated by @disable. The first oddity about @disable is it is attached to the foo(int), not the foo(ref int). If I wanted to know if foo(ref int) takes rvalue references, I'd have to go looking for the existence of another function. This is one of those cases where it's hard to prove a negative, as other functions can be introduced by mixins. This is a strong usability negative. Next, the @disable applies to the entire parameter list. However, overload selection is done by looking at each parameter. The DIP says: "The DIP author responded that ideas to improve this are welcome, but that he cannot imagine a use case." I can guarantee that the use case of more than one reference parameter will come up. The workarounds the DIP suggests are simply awful. Let's look at what C++ does for rvalue references: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html The syntax is attached to the parameter declaration of the function it applies to, not some other function, and not every parameter: int foo(T&& t); // C++ rvalue ref There are no weird workarounds, at least for that aspect. There are indeed unlikable things about the C++ rules, but the DIP needs to pay more attention to how C++ does this, and justify why D differs. Particularly because D will likely have to have some mechanism of ABI compatibility with C++ functions that take rvalue references. This is not a small problem. A further problem is implicit conversions, which the DIP ignores by only talking about ints. void bar(int); void foo(ref int); enum short s = 10; bar(s); // compiles foo(s); // currently fails to compile Should `s` be promoted to an int temporary, then pass the temporary by reference? I can find no guidance in the DIP. What if `s` is a uint (i.e. the implicit conversion is a type paint and involves no temporary)? Here's a discussion of Rust and rvalue references which may offer insight: https://www.reddit.com/r/rust/comments/3ko5pm/explaining_cs_rvalue_references_from_a_rust/ That the DIP applies to statements, not expressions. The DIP should not invent its own syntax, give no explanation of it, and have the reader guess. (It did explain the :=, but not the use of { } and statements.) And, even if one did a mental rewrite, the semantics of the statement version are simply wrong. (For example, if 'fun' was actually a function pointer returned by another function, and that other function threw an exception - then the destructor would be run on an uninitialized variable.) That the construction order issue is trivially fixable, by specifying the same behaviour as the non ref case modulo ref. It should never have gotten this far without giving a precise explanation of how exception safety is achieved when faced with multiple parameters. In the past I've done a lot of work on exception safety, and it isn't trivial once one goes beyond trivial cases. All that criticism aside, I'd like to see rvalue references in D. But the DIP needs significant work. For future reference, this is what a formal review should be. I'd also rather your exact words than some summarization of them.
Re: DIP 1016--ref T accepts r-values--Formal Assessment
On Thursday, 24 January 2019 at 23:18:11 UTC, kinke wrote: Proposed `out` semantics: --- void increment(out long value) { ++value; } increment(out value); --- vs. pointer version with current `out` semantics: --- void increment(long* pValue) { ++(*pValue); } increment(&value); --- The pointer workaround is both ugly (C) and unsafe (you can pass null). @safe void safestFunction() { int* ptr; increment(out *ptr); // can also pass null to ref/out even in @safe } It's probably going to be a hard sell to change the behavior of out now as well. It'd break quite a bit of code I think, did a search through druntime and phobos and quite a few functions use it. Maybe user code uses it less, I know I never use it.
Re: DIP 1016--ref T accepts r-values--Formal Assessment
On Thursday, 24 January 2019 at 20:01:45 UTC, kinke wrote: On Thursday, 24 January 2019 at 09:49:14 UTC, Manu wrote: We discussed and concluded that one mechanism to mitigate this issue was already readily available, and it's just that 'out' gains a much greater sense of identity (which is actually a positive side-effect if you ask me!). You have a stronger motivation to use 'out' appropriately, because it can issue compile errors if you accidentally supply an rvalue. `out` with current semantics cannot be used as drop-in replacement for shared in-/output ref params, as `out` params are default-initialized on entry. Ignoring backwards compatibility for a second, I think getting rid of that would actually be beneficial (most args are probably already default-initialized by the callee in the line above the call...) - and I'd prefer an explicitly required `out` at the call site (C# style), to make the side effect clearly visible. I'd have otherwise proposed a `@noRVal` param UDA, but redefining `out` is too tempting indeed. ;) Yah I'd like this approach way better and it could be an error if the function didn't assign the variable a value instead. Rather than just default initializing it. void test(out int value) { // error: value must be assigned a value } In the event the value passed isn't initialized. Can be dangerous if it is a struct with a destructor though. If they void initialized it, but I guess that's a problem with the current implementation as well.
Re: DIP 1016--ref T accepts r-values--Formal Assessment
On Thursday, 24 January 2019 at 09:24:19 UTC, Nicholas Wilson wrote: On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote: Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md void atomicIncrement(ref shared long x); atomicIncrement(myInt); Raises a good point, not covered by @disable where the intent is to modify it and modifying a temporary is wrong. `out ref` perhaps? Why isn't it covered by @disable ?
Re: DConf 2019: Shepherd's Pie Edition
On Sunday, 23 December 2018 at 10:07:40 UTC, Walter Bright wrote: BTW, another point for the presentations is that we cover the air fare and hotel expenses for the presenters. Quite a lot of people have been able to attend because of this. It's our way of giving a little bit back to strong contributors. I'll take that to assume you aren't paying for your own ticket, your own hotel expenses, etc. I wonder if you would feel differently about this if you had to pay for all these out of your own pocket. I'd be curious of the total expenses for DConf, all of the funds could be used to hire more developers. The pull request situation has improves significantly, I can only imagine what else could improve with those additional funds. I think it'd fair to outline how much does end up being spent on DConf and do a logical comparison of money being spent relatively. I know how you feel about owing people of the your community nothing though, so I guess it's a nice dream. Without those statistics to include with the argument it's pointless to argue with you, might as well be arguing whether unicorns exist.
Re: Blog post: What D got wrong
On Wednesday, 19 December 2018 at 19:58:53 UTC, Neia Neutuladh wrote: On Wed, 19 Dec 2018 17:28:01 +, Vijay Nayar wrote: Could you please elaborate a little bit more on this? In the linked program, I had expected that "ref" would return a reference to "a" that would behave similar to a pointer. They work like pointers that automatically dereference when assigning to the base type. Only three things in D can be ref: * A function parameter * A function return value * A foreach variable (since that's either going to be a function return value, a function parameter, or a pointer, depending on what you're iterating over) So when the compiler sees something like: ref int foo(); auto a = foo(); It sees that the type of 'a' has to be the same as the return type of 'foo'. Except that's not possible, so it uses the nearest equivalent type: int. And if you have: ref int foo(); int a = foo(); That obviously converts by copying the value. To be fair even in c++ this won't be a reference. int& foo(); auto a = foo(); // a == int auto& a = foo(); // a == int& So it shouldn't be that surprising.
Re: DIP 1015--Deprecation of Implicit Conversion of Int. & Char. Literals to bool--Formal Assement
On Wednesday, 14 November 2018 at 02:45:38 UTC, Walter Bright wrote: On 11/13/2018 3:29 PM, Rubn wrote: > enum A : int { a = 127 } `a` is a manifest constant of type `A` with a value of `127`. Remember that `A` is not an `int`. It is implicitly convertible to an integer type that its value will fit in (Value Range Propagation). Other languages do not have VRP, so expectations from how those languages behave do not apply to D. VRP is a nice feature, it is why: enum s = 100; // typed as int enum t = 300; // also typed as int ubyte u = s + 50; // works, no cast required, // although the type is implicitly converted ubyte v = t + 50; // fails In your articles, it is crucial to understand the difference between a manifest constant of type `int` and one of type `A`. At least can you understand where the problem lies? If you have code like this: foo(Enum.value); Then it gets changed: // ops might be calling a different function now foo(runtimeCond ? Enum.value : Enum.otherValue); Or how about if we just add another enum to our list: enum Enum : int { // ... // add new enum here, shifting the values down value, // 126 -> 127 otherValue, // 127 -> 128 - Ops now we are calling a different function ~somewhere~ // ... } From your implementation perspective I can see why it is a good thing. But from my user's perspective this just screams unreliable chaotic mess, even in the most trivial examples. What D does is only suitable for the absolute most trivial example: enum int s = 100; ubyte v = s; // ok no cast required But even just a slightly more trivial example like we have now, and it falls apart: enum int s = 100; void foo(int); void foo(byte); foo(s); // Not suitable for determining overloads // though is good for variable initialization Not one's really asking to add another layer to anything. Merely to not treat named enum types as if they are just constants like anonymous enums. ubyte a = Enum.value; // this is ok foo(Enum.value); // this needs to be x1000 more reliable
Re: DIP 1015--Deprecation of Implicit Conversion of Int. & Char. Literals to bool--Formal Assement
On Monday, 12 November 2018 at 22:07:39 UTC, Walter Bright wrote: One could have be treated as "better than" , and it sounds like a good idea, but even C++, not known for simplicity, tried that and had to abandon it as nobody could figure it out once the code examples got beyond trivial examples. I wonder what these examples are? What did C++ do instead, cause something tells me it didn't do what D is doing. An enum in C++ doesn't call different function overloads based on the constant value. The trivial examples with D's current implementation aren't even understood by most people it seems like.
Re: DIP 1015--Deprecation of Implicit Conversion of Int. & Char. Literals to bool--Formal Assement
On Monday, 12 November 2018 at 22:07:39 UTC, Walter Bright wrote: On 11/12/2018 12:34 PM, Neia Neutuladh wrote: Tell me more about this "consistency". int f(short s) { return 1; } int f(int i) { return 2; } enum : int { a = 0 } enum A : int { a = 0 } pragma (msg, f(a)); // calls f(int) pragma (msg, f(A.a)); // calls f(short) I.e. it's consistent. Here's how it works: f(a): `a` is a manifest constant of type `int`, and `int` is an exact match for f(int), and f(short) requires an implicit conversion. The exact match of f(int) is better. f(A.a): `a` is an enum of type `A`. `A` gets implicitly converted to `int`. The `int` then gets exact match to f(int), and an implicit match to f(short). The sequence of conversions is folded into one according to: => conversion> => conversion> Both f(int) and f(short) match, because implicit conversions rank the same. To disambiguate, f(short) is pitted against f(int) using partial ordering rules, which are: Can a short be used to call f(int)? Yes. Can an int be used to call f(short)? No. So f(short) is selected, because the "Most Specialized" function is selected when there is an ambiguous match. Note: the "most specialized" partial ordering rules are independent of the arguments being passed. --- One could have be treated as "better than" , and it sounds like a good idea, but even C++, not known for simplicity, tried that and had to abandon it as nobody could figure it out once the code examples got beyond trivial examples. This just seems like a bug to me. Any sane human being would expect all these functions to output the same thing. But it entirely depends on how you use it. import std.stdio; void foo(byte v) { writeln("byte ", v); } void foo(int v) { writeln("int ", v); } enum : int { a = 127 } enum A : int { a = 127 } void main() { A v = A.a; foo(A.a); // byte 127 < These two are probably the best showcase of what's wrong foo(v); // int 127 < same values being passed with same type but different result foo(a); // int 127 foo(127); // int 127 } https://run.dlang.io/is/aARCDo
Re: Funding for code-d/serve-d
On Saturday, 5 May 2018 at 11:21:29 UTC, Mike Parker wrote: This morning at the Hackathon I announced that the D Foundation is raising money for code-d/serve-d, the plugin for Visual Studio Code and its companion Microsoft Language Server Protocol implementation for D. We've set up a goal of $3000 at our Open Collective page: https://opencollective.com/dlang# At the top of the page, you'll see our current balance, the projected balance at the end of the year based on the current level of monthly donations, and the goal of $3000 on the far right. We're sitting at just over $1100 as I write this, which means we're almost halfway there already. We encourage those of you who use Webfreak's VS Code plugin to contribute whatever you can in order to fund its development, especially if you want to see it improve. An important point is that development on serve-d can ultimately benefit not just code-d, but other IDE and editor plugins where it may be used. If we reach $2700 within 30 days, the D Foundation will throw in the remaining $300 and we'll let Webfreak get to work. If this proves successful, we hope to use future goals to fund development across the D ecosystem. Some of our targets will be driven by the results of the recent State of D Survey and others will be driven by other concerns. If you are interested in helping to move the state of D development forward, please consider contributing! Any roadmap for what improvements will be made? It seems like it might have been a better choice to support dcd/dscanner/dfix. Most of the functionality is provided by those utilities for pretty much every IDE toolset out there, including code-d/serve-d.
Re: User Stories: Funkwerk
On Wednesday, 14 March 2018 at 14:17:50 UTC, Mike Parker wrote: foreach(auto element: elements) ":" is C++ syntax
Re: Beta 2.079.0
On Friday, 23 February 2018 at 00:05:59 UTC, Martin Nowak wrote: - each imported module should be on it's own line That's your opinion, my opinion is that importing 6 symbols from 6 different modules for a tiny cli tool sucks and bloats code example. So the alternative is to not use selective imports, but only those and static imports have a clear path to become lazy (https://issues.dlang.org/show_bug.cgi?id=13255). This can be solved by adding a package.d to phobos. This doesn't need a language change that introduces more ambiguities for such a subjective matter as "I'm too lazy to write 5 more lines of code". 5 Lines of code is hardly bloat for a code example. Example code is better to follow best practices, or you get the whole `using namespace std` situation like you do with C++. The people that actually can comprehend won't be hindered by it, and everyone else it won't make a difference cause they have no understanding of what they are doing and just copy and paste the code.