May 12, 2022
On Thu, May 12, 2022 at 02:42:43PM +0000, Anonymouse via Digitalmars-d-learn wrote: [...]
> The one thing that has caused me most anguish and woe is hands-down https://issues.dlang.org/show_bug.cgi?id=18026 though. It hasn't bit me for a while now, but the feeling of uncertainty, that the compiler might just suddenly after an innocent change no longer compile your project, seemingly outside of your control, is just... disheartening when it happens.

static foreach isn't meant to handle large loops. Writing `static foreach (i; 0 .. 60000)` is generally a bad idea; my suspicion is that the compiler ran out of stack space).  It's more for unfolding groups of statements or declarations like cases in a switch-statement.

For complex loops, what you really want is to use CTFE, which has a proper interpreter that can execute real code, instead of static foreach. What I'd do in the case described in comment 20 is to use CTFE to generate an array of indices that satisfy the predicate (it can create this array however it wants), then static foreach over this array, instead of iterating from 0 to 60000 directly.

Or, in certain cases, you might want to just try straight foreach instead of static foreach, just make a tuple of your indices first and it will auto-unroll.


T

-- 
Не дорог подарок, дорога любовь.
May 12, 2022
On Thursday, 12 May 2022 at 16:24:26 UTC, Ali Çehreli wrote:
>
> Cool trick but "parent" confused me there. I think you mean "base". :)

https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming

mentions "base class" as much as "parent class"
May 12, 2022
On Wed, May 11, 2022 at 06:43:39PM +0000, Guillaume Piolat via Digitalmars-d-learn wrote:
> On Wednesday, 11 May 2022 at 05:41:35 UTC, Ali Çehreli wrote:
> > What are you stuck at? What was the most difficult features to understand? etc.
> 
>     - How to do deterministic destruction with programs that use
>     everything (struct / class / dynamic dispatch / GC / manual /
>     etc). This requires to understand what the runtime does, what the
>     gc does.

I'm not sure I fully understand what you're trying to say here. How does GC mix with deterministic destruction?  I thought by the very nature of GC, destruction is non-deterministic.  If you want deterministic destruction, don't use the GC...?


>       Interesting nonetheless.
> 
>     - Some traps. Accidental TLS is a thing, top-level should probably
>     not be silently TLS.
>       People will loose hours on this completely preventable thing.
>       What was the idea, optimize code without people knowing?

Why is TLS by default a problem?

It's not really for optimization, AIUI, it's more for thread safety: module-global state is TLS by default, so you don't accidentally introduce race conditions.


>     - `shared static this()` vs `static this()` is another trap.

One is per-process, one is per-thread.  Why is this a trap?


[...]
>     - Some features lack an escape hatch, notably `pure`. pure leaks
>     into identifiers, like `pureMalloc`. Trying to add `pure` fails on
>     a large codebase.

IMNSHO, pureMalloc is a code smell. It should not have been added in the first place.

But more to the point: pure is of limited utility. Perhaps the most useful application is the initialization of immutable data structures constructed by a mutable factory method. But other than that, I don't find very much use for it in practice.  I wouldn't sweat it if I couldn't easily add `pure` to an entire codebase -- it hardly makes any difference anyway.


>     - `@safe`/`@trusted`/`@system` is good but the definition of what
>     `@trusted` means has to be remembered from the programmer.

But isn't that the nature of all escape hatches?  An escape hatch by definition means you're operating outside of the abstractions provided by the compiler; IOW you're on your own and you take responsibility for any problems that you may inadvertently introduce by using the escape hatch.


>       For example `Mutex.lock()` is `@trusted`, it could have been
>       `@system` to let user review their usage of locks. You have to
>       wonder "can a lock()/unlock() corrupt memory?". People can use
>       that to mean "@reviewed" instead. Because it is up to us, the
>       exact meaning will float in the D subcultures. A function which
>       has been marked `@trusted` does not receive any review whan
>       changed later. It will not mean the same as `@trusted` in
>       another codebase.

IMNSHO, @trusted should never be used in public-facing APIs. It's really an implementation detail -- "this code does something using potentially dangerous operations, but we reviewed it carefully to make sure it's safe to call from @safe code".  The caller does NOT need to know this; as far as the caller is concerned, it's calling a @safe function. That's all it knows and all that it should care about. How this @safe function is implemented -- using completely @safe operations or potentially dangerous operations (@trusted) isn't something the user should care about.  That's something the author of the module needs to care about, but it's none of the users' business.

IOW, public APIs should always be @safe or @system. @trusted should only appear on internal APIs.


>     - Generic code typically has bad names (domain-less) and worse
>     usability. It's often not pretty to look at. Mostly cultural,
>     since D has powerful templates so they had to be everywhere. UFCS
>     chains are not that convincing when you are worried about
>     maintenance.

I'm puzzled by this. I use (and write!) generic code all the time and they have been great. UFCS chains are awesome; they allow me to express a series of data transformations in a very concise way, so that I can keep the high-level logic of the function readable, without having to break it into separate functions.  This, plus `auto` type inference, makes the code *more* maintainable, IME, because I can shuffle components of the UFCS chain around without needing to rewrite the types of a bunch of helper functions.

So I'm curious, what exactly is it about UFCS chains that make it less maintainable?


>       Phobos take short names for itself, this leads to pretty
>       complicated operations having a small screen estate.

I'm also puzzled by this. Why is this a problem?


>     - `assert(false)` being different and not removed by `-release`.
>     Keyword reuse seems entrenched but honestly a "crash here" keyword
>     would be more readable.

	noreturn crashHere() { assert(false); }

	void main() {
		...

		crashHere; // ;-)
	}


[...]
> Otherwise D is glorious and get syntax and usability right, which puts it ahead of almost every other language.

Can't argue with that.


T

-- 
MACINTOSH: Most Applications Crash, If Not, The Operating System Hangs
May 12, 2022
On Thu, May 12, 2022 at 01:06:02AM +0000, Christopher Katko via Digitalmars-d-learn wrote: [...]
> Cool useful library functions like sumElement that magically don't work on static arrays.

Just slice it with []:

	int[5] data = [ 1, 2, 3, 4, 5 ];
	auto sum = data[].sumElement;

The main reason is that static arrays cannot shrink (their length is constant), so they don't qualify as ranges. No problem, [] takes a slice of them that *can* shrink.


> 'private' is per module, not per class, making it pretty much useless for preventing incorrect access and using .

This is a common complaint.  But IME, I haven't really run into problems with it.  Accessing private members really only becomes a problem when you have multiple separate modules interacting with each other. If a module has grown large enough that this starts becoming a problem, it's usually a sign that it's time to refactor the module into two (or more) smaller ones.


> completely different semantics for a class vs a struct. Is it a reference?  Is it a value? Look up the entire declaration and have the entire Dlang manual open to find out.

class == by reference
struct == by value

Very straightforward.


> As far as I remember, no automatic RAII support, even though it's insanely useful. You have to manually write scope(end) stuff which means any person forgetting one is now leaking memory.

???  Structs with dtors have RAII. You only need scope(exit) if you're
manually managing resources.

What exactly are you referring to here?


> Writing output in a deconstuctor (for learning) works. But then you accidentally combine two strings inside it and the garbage collecter crashes without a stack trace.

Yeah, class dtors and GC don't mix, in general. There was a proposal to remove class dtors from the language some years ago. But it didn't happen, probably because more people complained about that than about dtors not being allowed to allocate memory or access GC'd resources. :-D


> Documentation. Documentation. Documentation.
[...]

What exactly is wrong with the docs?  A concrete list of actionable items would be nice. (I admit the docs could use some improvements, btw.)


T

-- 
"I'm not childish; I'm just in touch with the child within!" - RL
May 12, 2022
On Thu, May 12, 2022 at 09:04:09AM -0700, Ali Çehreli via Digitalmars-d-learn wrote:
> On 5/11/22 18:06, Christopher Katko wrote:
> 
> > Cool useful library functions like sumElement that magically don't work on static arrays.
> 
> Yeah, that sometimes gets me as well. Although it is trivial to deal with, the programmer may be surprised by the strange error messages:
> 
>   int[3] arr = [ 1, 2, 3 ];
>   assert(sum(arr) == 6);
> 
> Error: template `std.algorithm.iteration.sum` cannot deduce function from
> argument types `!()(int[3])`
> /usr/include/dlang/dmd/std/algorithm/iteration.d(7234): Candidates are:
> `sum(R)(R r)`
>   with `R = int[3]`
>   must satisfy the following constraint:
> `       isInputRange!R`
> 
> WHAT? :) But the clue is on the last line above.
[...]

Seriously though, that error message is horrendously ugly.  I mean I've seen it thousands of times by now, so I know what it means and where to look for the actual problem. But it's eminently unfriendly to someone who doesn't already know the language very well.


T

-- 
A mathematician learns more and more about less and less, until he knows everything about nothing; whereas a philospher learns less and less about more and more, until he knows nothing about everything.
May 12, 2022
On Thursday, 12 May 2022 at 18:07:05 UTC, H. S. Teoh wrote:
> On Thu, May 12, 2022 at 09:04:09AM -0700, Ali Çehreli via Digitalmars-d-learn wrote:
>> Error: template `std.algorithm.iteration.sum` cannot deduce function from
>> argument types `!()(int[3])`
>> /usr/include/dlang/dmd/std/algorithm/iteration.d(7234): Candidates are:
>> `sum(R)(R r)`
>>   with `R = int[3]`
>>   must satisfy the following constraint:
>> `       isInputRange!R`
>> 
>> WHAT? :) But the clue is on the last line above.
> [...]
>
> Seriously though, that error message is horrendously ugly.  I mean I've seen it thousands of times by now, so I know what it means and where to look for the actual problem. But it's eminently unfriendly to someone who doesn't already know the language very well.

Good news: starting from DMD 2.099, this error message has been reworded. Instead of "cannot deduce function...". it now says:

Error: none of the overloads of template `std.algorithm.iteration.sum` are callable using argument types `!()(int[3])`
May 12, 2022

On Thursday, 12 May 2022 at 16:48:05 UTC, H. S. Teoh wrote:

>

static foreach isn't meant to handle large loops. Writing static foreach (i; 0 .. 60000) is generally a bad idea; my suspicion is that the compiler ran out of stack space). It's more for unfolding groups of statements or declarations like cases in a switch-statement.

I understand, but I don't think I had any static foreaches in my code at the time. My case was more comment #10.

May 12, 2022

On Thursday, 12 May 2022 at 15:17:10 UTC, Adam D Ruppe wrote:

>

It is simpler than it looks, I wrote about it in my book and in a post here:

https://forum.dlang.org/post/xklcgjaqggihvhctczxx@forum.dlang.org

"Then commas separate the definitions of each placeholder variable, just as if they were template argument definitions [...]"

That... makes sense, I didn't think of them like that.

May 12, 2022
On 5/12/22 12:00, Paul Backus wrote:

> Good news: starting from DMD 2.099, this error message has been
> reworded. Instead of "cannot deduce function...". it now says:
>
> Error: none of the overloads of template `std.algorithm.iteration.sum`
> are callable using argument types `!()(int[3])`

Much better.

An idea: The compiler should be able to detect static arrays and also suggest slicing with something like "slicing as in `arr[]` might work".

Ali

May 12, 2022
On Thursday, 12 May 2022 at 16:04:09 UTC, Ali Çehreli wrote:
> My view on private has changed over the years. I need to be convinced that there is usage that needs to be protected. :) I don't see people using types freely especially the ones that are in the same module. The only argument for private is to allow changing the implementation of published libraries in the future.

I use private as part of my rapid dev process. You write code, you get things working with no real worry for correctness or careful interfaces. You cannot make an interface until you actually know what you're making.

So you make things, with "bad" connections. Then you remove those connections.

1. Get system working with lots of direct access to class variables.
2. Make those variables forbidden (through private in C++).
3. The compiler now shows you every instance of your new interface encapsulation violations. No human decision to opt-in. No remembering to search. You have an automatically generated list of violations to fix.

I do the same thing with a module called "g" (for globals). I write new code into g, get it working. I can see how "dirty" a file is by simply searching for how many references to the module g there are. Then if I move the code into a proper new module, all references to g magically fail. It is impossible for me to leave dangling old code touching naughty internals, and I get a nice error view of all areas that need attention. If the uses are all over the place and not in only a few areas (instead of just in logic() and draw(), but all over the place) then I know I need to rewrite and introduce a system so everything is mostly in one place.

In D, I can do the module based method, but nothing short of renaming variables gives me a list of violations and, that also makes all the correct internal accesses wrong. Because private doesn't work.

Call it whatever keyword you want, I really want a 'private' specifier for classes. It's incredibly useful.