August 21, 2023
On 8/21/2023 2:04 PM, Jonathan M Davis wrote:
> That being said, fortunately, most D code doesn't have to worry about
> version statements much, because druntime hides most of that mess. There are
> other libraries that wrap C code and/or are more low-level in nature which
> have to do a fair bit of versioning with code, but most D code is able to be
> version-agnostic, which fortunately keeps the issues with how how version
> statements work to a minimum.

Thanks for your thoughts on this. One more meta thing about versioning is it is better to remove all the version conditionals from the core parts of the program.

I.e. abstract away what the versioning is trying to represent, and put those abstractions in a separate module.

Over time I've gotten better at this, and it makes the code much more pleasing to read.

I'll be talking about this and similar things at DConf.

August 22, 2023
On Monday, 21 August 2023 at 19:27:57 UTC, Walter Bright wrote:
> On 8/21/2023 11:46 AM, Basile B. wrote:
>> However that approach was used. It really looks like an alternative to a version OrOr. There are plenty of other examples in drutime sources.
>
> Druntime was written by many people, and does not exhibit consistent best practices.
>
>> Sure at some point there is also the problem of the unix standards...Every distribution can choose its own value for the flag of this func or that one...which creates complexity in C `define`s, but should not in D. There is a problem that is not solved.
>
> I've dealt with enough professional C .h files to conclude that #ifdef algebra is the worst solution. This includes the ones I have written.
>
> The objectives of minimizing keystrokes and maximizing clarity are only rarely aligned.
>
> I understand that many of D's design decisions are out of step with conventional wisdom. They aren't the result of reading about best practices, but instead decades of living with those practices.

There are multiple problems with the #ifdef algebra.
The prohibition against boolean expressions in version conditionals is a good one and fixes some of the problems.
But #ifdef also allows horribly nested conditionals, and D doesn't stop that.
Also D doesn't deal with versions defined on the command line.

The whole algebra of "defined" vs "undefined" is a sloppy error-prone concept that is out of place in a statically typed language. It's like PHP or something.
In that original example, a typical typo would be MICOBLAZE. That could go undetected for years. A statically typed language should catch that.

Years ago I proposed that versions should be considered to be bools in a kind of compile-time only 'version' scope and obey normal bool rules. You could then allow boolean expressions in version _declarations_ (not in conditionals).

In the original example you would define something like:

version DEFAULT_MS_ENUMS = MICROBLAZE || SH || ARM_Any || IBMZ_Any || IA_64 || M68K || TILE || X86_Any || RISCV_Any || SPARC_Any || PPC_Any;

The syntax I suggested was something like:
version <version_identifier> = <version_expression>;
version <version_identifier> = true;  // same as the existing version = <version_identifier>; but this would only be used in testing I think
version <version_identifier> = extern;  // for something defined on the command line
version <version_identifier> = default; // for a compiler built-in

where <version_expression> is a boolean expression of <version_identifier> using || and &&.

If additionally you disallowed version declarations and version statements inside version conditionals, you'd have a one-definition rule and it would be impossible to create a rat's nest. (You could still create a rat's nest with static if, but not with version).

- Don.ghostof
/disappears again into the mist
August 22, 2023
On Tuesday, 22 August 2023 at 01:46:17 UTC, Walter Bright wrote:
> Thanks for your thoughts on this. One more meta thing about versioning is it is better to remove all the version conditionals from the core parts of the program.
>
> I.e. abstract away what the versioning is trying to represent, and put those abstractions in a separate module.
>
> Over time I've gotten better at this, and it makes the code much more pleasing to read.
>
> I'll be talking about this and similar things at DConf.

There's a 1992 article on writing portable C programs that comes to similar conclusions:

https://www.usenix.org/legacy/publications/library/proceedings/sa92/spencer.pdf
August 22, 2023
On 8/22/2023 3:24 AM, Don wrote:
> where <version_expression> is a boolean expression of <version_identifier> using || and &&.

Still really trying to get away from that.

> 
> If additionally you disallowed version declarations and version statements inside version conditionals, you'd have a one-definition rule and it would be impossible to create a rat's nest. (You could still create a rat's nest with static if, but not with version).

More key features of version:

1. the versions of a module cannot be imported by another module, i.e. they are irredeemably private

2. versions cannot be redefined once they are "read". This prevents the chicken-and-egg problem, while still allowing them in conditionals

August 22, 2023

Since i got confused with version(linux) and version(Linux), i try to limit my use of that kind of code to a strict minimum.. but sometimes it just is not possible to avoid using it

I wish it was represented as an enum, similar to how compiler do the __FILE__

But for that to work, D would need to support .enum so you wouldn't need to import anything

In D that would look like:

enum STUFF = static switch(__OS__) {
    case OS.Linux:
    break;
    default:
    break;
};

In a sane language that would look like:

enum STUFF = switch(__OS__) {
    .Linux => (..);
    else => (..);
};

Funny enough i requested something similar months ago

https://forum.dlang.org/thread/bnwbsqsyvcyhjublipxk@forum.dlang.org

The reason why people don't understand the motive is because they can't conceptualize a better syntax yet and think the C's way of doing switch is the immutable status quo, i nulify it because i have choice, nowadays people have choice

tagged union, tuples, .enum, pattern matching, essential features in a post C world

August 23, 2023
On Tuesday, 22 August 2023 at 18:43:40 UTC, Walter Bright wrote:
> 1. the versions of a module cannot be imported by another module, i.e. they are irredeemably private

Except the ones specified on the command line, which are irredeemably superglobal.

I wish the command line ones were also scoped somehow.
August 23, 2023

On Tuesday, 22 August 2023 at 22:03:41 UTC, ryuukk_ wrote:

>

The reason why people don't understand the motive is because they can't conceptualize a better syntax yet and think the C's way of doing switch is the immutable status quo

Walter already posted a sum type proposal. We do get it. He also mentioned that match would need a separate DIP:
https://forum.dlang.org/post/tm4jr9$219h$1@digitalmars.com

August 23, 2023
On 8/23/2023 5:28 AM, Adam D Ruppe wrote:
> Except the ones specified on the command line, which are irredeemably superglobal.

That's deliberate, because the Bob and Carol versions should not co-exist in the same executable. You can always create a BobAndCarol version for those modules that have a foot in both versions.

C's -D, -U switches and #define/#undef system is infinitely adjustable, and programmers inevitably take full advantage of that to create an incomprehensible soup.

You can also create a config.d file with a list of enum declarations in them, import that, and use whatever algebra you want with static if. I recommend finding a way to use versions instead.
August 23, 2023
On 8/22/2023 10:57 AM, Paul Backus wrote:
> There's a 1992 article on writing portable C programs that comes to similar conclusions:
> 
> https://www.usenix.org/legacy/publications/library/proceedings/sa92/spencer.pdf

I didn't know about this paper, well done!
August 23, 2023
On Wednesday, 23 August 2023 at 17:09:09 UTC, Walter Bright wrote:
> That's deliberate, because the Bob and Carol versions should not co-exist in the same executable.

Well, they do anyway. And then you get random name conflicts across different libraries.

lib a uses version(fast) to do something. lib b uses version(fast) to do something different.

dmd -version=fast affects lib a and b together.

So then people get clever and do

dmd -c -version=fast liba.d
dmd -c libb.d
dmd liba.obj libb.obj main.d

now version fast and non-fast can exist together, since the separate compilation gave a bit of isolation. But...

---
module liba;
struct A {
   version(fast)
       int a;
   int b;
}

void foo1(A b) {}
// --------
module libb;
struct B {
   version(fast)
       int a;
   int b;
}

void foo2(B b) {}
---

And then you

---
import liba;
import libb;

void main() {
     foo1(A(4));
     foo2(B(4));
}
---


and guess what? things explode since now there's an abi mismatch. There is no way to compile the main module to use both the precompiled liba and libb together (except writing new bindings that match the abi by hand, of course you could do that).


This kind of thing has happened even with Phobos.


D's `version` specifier is a major failure in real world code, leading to incomprehensible soup in addition to runtime corruption, porting trouble, and other problems.

C's system sucks, but D's does too, in ways that are not as well known as C's.

> You can also create a config.d file with a list of enum declarations in them, import that, and use whatever algebra you want with static if.

This is *significantly* better than using D's failed `version` specifier.