digitalmars.D - No need for version expression is a lie
- Basile B. (67/67) Aug 21 2023 from core.sys.posix.sys.mman:
- Walter Bright (4/5) Aug 21 2023 Spelling it out for each variation makes it easy to read.
- Walter Bright (4/5) Aug 21 2023 I would not use the DEFAULTS, but would spell it out for each system. Th...
- Basile B. (2/8) Aug 21 2023 you have to read top down to see what DEFAULTS do.
- Walter Bright (2/11) Aug 21 2023 That's why I wouldn't use the DEFAULTS approach. Just make a table.
- Basile B. (9/22) Aug 21 2023 However that approach was used. It really looks like an
- Walter Bright (9/15) Aug 21 2023 I've dealt with enough professional C .h files to conclude that #ifdef a...
- Max Samukha (2/4) Aug 21 2023 "best practices" is undefined
- Hipreme (24/44) Aug 21 2023 I had the same opinion as the OP before, but one thing Walter is
- Don (38/58) Aug 22 2023 There are multiple problems with the #ifdef algebra.
- Walter Bright (7/14) Aug 22 2023 More key features of version:
- Adam D Ruppe (4/6) Aug 23 2023 Except the ones specified on the command line, which are
- Walter Bright (10/11) Aug 23 2023 That's deliberate, because the Bob and Carol versions should not co-exis...
- Adam D Ruppe (50/55) Aug 23 2023 Well, they do anyway. And then you get random name conflicts
- Walter Bright (15/19) Aug 23 2023 C's #ifdef is not better in any way, and much much worse in many ways.
- Walter Bright (4/4) Aug 23 2023 I don't know how to design a language to force people to write better co...
- Adam D Ruppe (33/38) Aug 24 2023 version(unittest) made waves some time ago, making all kinds of
- Walter Bright (42/74) Aug 24 2023 I'll read it, thanks.
- Adam D Ruppe (30/40) Aug 24 2023 No, it is a case of the same general problem. It even literally
- Walter Bright (23/27) Aug 24 2023 Not impossible, just initially a mite tedious. One can still incremental...
- Dukc (21/26) Aug 24 2023 I understand this point, but it doesn't take out what Adam said.
- Adam D Ruppe (12/14) Aug 24 2023 The top level static assert has no value.
- Richard (Rikki) Andrew Cattermole (25/25) Aug 24 2023 There is an alternative solution which would be much simpler.
- Adam D Ruppe (13/18) Aug 24 2023 You can kinda achieve this with a zero arg template today:
- Walter Bright (2/9) Aug 24 2023 The static assert will fail at compile time. `assert(0)` will fail at ru...
- Richard (Rikki) Andrew Cattermole (4/16) Aug 24 2023 Currently yes. I proposed a change where it requires some form of usage
- Adam D Ruppe (94/111) Aug 24 2023 So your solution to how static assert(0) sucks is to literally
- Walter Bright (6/38) Aug 24 2023 If you want to do it that way, that's ok. I just want to head off the
- An Pham (18/37) Aug 24 2023 For import or declaration, I refer to use
- Adam D Ruppe (4/6) Aug 24 2023 This is still going to stop compilation so it doesn't scale very
- Walter Bright (10/12) Aug 24 2023 Unfortunately, when compiling a library with one set of switches and lin...
- John Colvin (7/12) Aug 24 2023 We have the ability to declare information (`enum`s and so on) &
- Adam D Ruppe (5/9) Aug 24 2023 Yeah, it would be kinda interesting if `core.build_environment`
- Walter Bright (7/13) Aug 24 2023 At one point, druntime was largely converted to using enums and static i...
- Adam D Ruppe (4/10) Aug 24 2023 It also tends to lag adoption on new platforms by a significant
- Walter Bright (4/14) Aug 24 2023 Beats me. Long time ago. I think stdc.stdio was one of the afflicted, so...
- Jonathan M Davis (43/66) Aug 21 2023 The way that version statements try to enforce a lack of boolean logic
- Walter Bright (8/14) Aug 21 2023 Thanks for your thoughts on this. One more meta thing about versioning i...
- Paul Backus (4/12) Aug 22 2023 There's a 1992 article on writing portable C programs that comes
- Walter Bright (2/6) Aug 23 2023 I didn't know about this paper, well done!
- matheus (6/10) Aug 28 2023 Love this part: ..."Many of the problems we discuss stem from the
- Basile B. (5/11) Aug 24 2023 I've been informed that you are not actually perfectly satisfied
- Walter Bright (2/6) Aug 24 2023 I'm always experimenting with better ways to do things.
- ryuukk_ (32/32) Aug 22 2023 Since i got confused with ``version(linux)`` and
- Nick Treleaven (4/7) Aug 23 2023 Walter already posted a sum type proposal. We do get it. He also
- Walter Bright (3/6) Aug 23 2023 Yes, and it's paused for the moment as we're working on fixing issues ra...
- Walter Bright (3/4) Aug 23 2023 For those who wonder why "linux", it is to match C's predefined identifi...
- Kagamin (16/33) Aug 27 2023 Like this?
from core.sys.posix.sys.mman: ```d version (linux) { enum MAP_SHARED = 0x01; enum MAP_PRIVATE = 0x02; enum MAP_FIXED = 0x10; enum MAP_FAILED = cast(void*) -1; version (MICROBLAZE) private enum DEFAULTS = true; else version (Alpha) { private enum DEFAULTS = false; enum MAP_ANON = 0x10; enum MS_ASYNC = 1; enum MS_SYNC = 2; enum MS_INVALIDATE = 4; } else version (SH) private enum DEFAULTS = true; else version (ARM_Any) private enum DEFAULTS = true; else version (IBMZ_Any) private enum DEFAULTS = true; else version (IA64) private enum DEFAULTS = true; else version (HPPA_Any) { private enum DEFAULTS = false; enum MAP_ANON = 0x10; enum MS_SYNC = 1; enum MS_ASYNC = 2; enum MS_INVALIDATE = 4; } else version (M68K) private enum DEFAULTS = true; else version (TILE) private enum DEFAULTS = true; else version (X86_Any) private enum DEFAULTS = true; else version (MIPS_Any) { private enum DEFAULTS = false; enum MAP_ANON = 0x0800; enum MS_ASYNC = 1; enum MS_INVALIDATE = 2; enum MS_SYNC = 4; } else version (RISCV_Any) private enum DEFAULTS = true; else version (SPARC_Any) private enum DEFAULTS = true; else version (PPC_Any) private enum DEFAULTS = true; else static assert(0, "unimplemented"); static if (DEFAULTS) { enum MAP_ANON = 0x20; enum MS_ASYNC = 1; enum MS_INVALIDATE = 2; enum MS_SYNC = 4; } } ``` Like you pretend that `enum DEFAULTS` is not a workaround the limitations.
Aug 21 2023
On 8/21/2023 8:39 AM, Basile B. wrote:from core.sys.posix.sys.mman:Spelling it out for each variation makes it easy to read. With C system headers, I often have to run the preprocessor over it and examine the resulting file to decipher what's really happening with the algebra soup.
Aug 21 2023
On 8/21/2023 8:39 AM, Basile B. wrote:Like you pretend that `enum DEFAULTS` is not a workaround the limitations.I would not use the DEFAULTS, but would spell it out for each system. Then it looks like a table, with rows and columns, and makes it easy to see which setting is for which system.
Aug 21 2023
On Monday, 21 August 2023 at 16:19:25 UTC, Walter Bright wrote:On 8/21/2023 8:39 AM, Basile B. wrote:you have to read top down to see what DEFAULTS do.Like you pretend that `enum DEFAULTS` is not a workaround the limitations.I would not use the DEFAULTS, but would spell it out for each system. Then it looks like a table, with rows and columns, and makes it easy to see which setting is for which system.
Aug 21 2023
On 8/21/2023 9:26 AM, Basile B. wrote:On Monday, 21 August 2023 at 16:19:25 UTC, Walter Bright wrote:That's why I wouldn't use the DEFAULTS approach. Just make a table.On 8/21/2023 8:39 AM, Basile B. wrote:you have to read top down to see what DEFAULTS do.Like you pretend that `enum DEFAULTS` is not a workaround the limitations.I would not use the DEFAULTS, but would spell it out for each system. Then it looks like a table, with rows and columns, and makes it easy to see which setting is for which system.
Aug 21 2023
On Monday, 21 August 2023 at 18:01:20 UTC, Walter Bright wrote:On 8/21/2023 9:26 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. 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.On Monday, 21 August 2023 at 16:19:25 UTC, Walter Bright wrote:That's why I wouldn't use the DEFAULTS approach. Just make a table.On 8/21/2023 8:39 AM, Basile B. wrote:you have to read top down to see what DEFAULTS do.Like you pretend that `enum DEFAULTS` is not a workaround the limitations.I would not use the DEFAULTS, but would spell it out for each system. Then it looks like a table, with rows and columns, and makes it easy to see which setting is for which system.
Aug 21 2023
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.
Aug 21 2023
On Monday, 21 August 2023 at 19:27:57 UTC, Walter Bright wrote:Druntime was written by many people, and does not exhibit consistent best practices."best practices" is undefined
Aug 21 2023
On Monday, 21 August 2023 at 19:27:57 UTC, Walter Bright wrote:On 8/21/2023 11:46 AM, Basile B. wrote:I had the same opinion as the OP before, but one thing Walter is correct is that it is better for you to define version presets. One thing I can't agree is the `static assert` approach, if you have a really big library, partially porting it makes it quite unfeasible, having ways to just test is a really nice feature. Also, for not being that verbose, like `if(a) b = true; if(c) b = true`, I would rather just put the defaults and only change the cases which are different. One thing that also works even better IMO is using: ```d version(MICROBLAZE) version = UseDefault; else version (SH) version = UseDefault; else version (ARM_Any) version = UseDefault; else version (IBMZ_Any) version = UseDefault; else version (IA64) version = UseDefault; version(UseDefault) private enum DEFAULTS = true; ``` This pattern has been work really well for me and it makes easier read than only thinking about the "table approach"(unless we really had a table). This also makes your code a lot more consistent. You can even save your version combination as a `static if` thing (and try to use it as if it were a version).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.
Aug 21 2023
On Monday, 21 August 2023 at 19:27:57 UTC, Walter Bright wrote:On 8/21/2023 11:46 AM, Basile B. wrote: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 mistHowever 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.
Aug 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
Aug 22 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 privateExcept the ones specified on the command line, which are irredeemably superglobal. I wish the command line ones were also scoped somehow.
Aug 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.
Aug 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.
Aug 23 2023
On 8/23/2023 10:24 AM, Adam D Ruppe wrote:This kind of thing has happened even with Phobos.Can you be more specific? I'm curious.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 #ifdef is not better in any way, and much much worse in many ways. For library writers, there are indeed some things to watch out for. I recommend using the name of the library as a prefix on their version identifiers. For local versions, one can use private enums with static if. Phobos has some head-scratchers, like this in std.stdio: ``` else version (CRuntime_DigitalMars) { // Specific to the way Digital Mars C does stdio version = DIGITAL_MARS_STDIO; } ``` https://github.com/dlang/phobos/pull/8802
Aug 23 2023
I don't know how to design a language to force people to write better code, all I can do is make it harder to write worse code. Then try to lead by example: https://github.com/dlang/phobos/pull/8803 https://github.com/dlang/phobos/pull/8802
Aug 23 2023
On Thursday, 24 August 2023 at 04:53:49 UTC, Walter Bright wrote:Can you be more specific? I'm curious.version(unittest) made waves some time ago, making all kinds of random errors when user code was trying to be tested but still imported phobos modules.For library writers, there are indeed some things to watch out for. I recommend using the name of the library as a prefix on their version identifiers.Yeah, that's what I tend to do too, but it is just a convention. The language probably could have defined versions to be specific to a module, even if given on the command line. But name conflicts are one of the smallest problems with them. When a mismatch on compile and import lead to ABI mismatches are the worst. I wrote at greater length about this here: http://dpldocs.info/this-week-in-d/Blog.Posted_2022_11_14.html#redesign-for-template-emission-woesPhobos has some head-scratchers, like this in std.stdio:This specific one doesn't seem to make sense, but this pattern comes up a lot because you might want to use a specific implementation from multiple triggers. Easy to imagine there might be a CRuntime_DigitalMars and a CRuntime_DigitalMars64, for example, different predefined versions that share the implementation of this portion. But other parts of the module might branch differently on CRuntime_DigitalMars vs DM64, so you couldn't just find/replace all if you decided to add the other one. So the author there is probably doing this to try to future-proof the code with some extra semantic tagging in case some other definition is added. I've done this a few times myself. It actually isn't all bad, but it can get ugly when someone specifies `-version=DIGITAL_MARS_STDIO` on the command line when building against Microsoft C runtime... then what happens? Or when someone again tries to figure proof and puts `else static assert(0)` at the end, making incremental porting impossible. (which i also wrote about in the last year: http://dpldocs.info/this-week-in-d/Blog.Posted_2023_02_20.html#static-assert-patterns-arguably harmful-for-porting ) C's ifdef is a big mess. D's version is a different big mess, but still a mess.
Aug 24 2023
On 8/24/2023 8:30 AM, Adam D Ruppe wrote:On Thursday, 24 August 2023 at 04:53:49 UTC, Walter Bright wrote:That was something different.Can you be more specific? I'm curious.version(unittest) made waves some time ago, making all kinds of random errors when user code was trying to be tested but still imported phobos modules.I wrote at greater length about this here: http://dpldocs.info/this-week-in-d/Blog.Posted_2022_11_14.html#redesign-for-template-emission-woesI'll read it, thanks.I wouldn't set it up that way. I'd version on IA64 within CRuntime_DigitalMars. I.e. overlapping version declarations are not a good plan, for precisely the reason you mention. Better to version along orthogonal axes.Phobos has some head-scratchers, like this in std.stdio:This specific one doesn't seem to make sense, but this pattern comes up a lot because you might want to use a specific implementation from multiple triggers. Easy to imagine there might be a CRuntime_DigitalMars and a CRuntime_DigitalMars64, for example,different predefined versions that share the implementation of this portion. But other parts of the module might branch differently on CRuntime_DigitalMars vs DM64, so you couldn't just find/replace all if you decided to add the other one. So the author there is probably doing this to try to future-proof the code with some extra semantic tagging in case some other definition is added. I've done this a few times myself.Back in the 80s, everyone knew that 32 bits was inevitable. So the professionals future-proofed their code so it would be portable from 16 to 32 bits. (A classic example of this is Windows with their forest of typedefs like BYTE.) But when 32 bits arrived, people discovered that their future-proofing did not work at all. It failed because none of them had ever ported code from 16 to 32 before, and so the future-proof solutions were all the wrong solutions. The same thing happened to me. But I learned how to make code 16->32 portable, and it was pretty simple. Just not at all like what I or anyone else presumed it would be. Have you noticed how darned easy it is to port D code between 32 and 64 bit? That's the result of my experience porting 16 to 32. The point is, future-proofing is a pretty hard thing to do, because we simply don't know what the future will be. This is why I don't really try to future-proof with versions, instead relying on enumerating the known cases, and leaving the default with `static assert(0);`It actually isn't all bad, but it can get ugly when someone specifies `-version=DIGITAL_MARS_STDIO` on the command line when building against Microsoft C runtime... then what happens?At some point, you're just going to have to run: grep -r version * Fortunately (by design!) you can grep for such simple patterns. I learned that long ago when Matthew Wilson suggested I extend the DMC++ compiler to use a keyword for casting rather than (type) which is ungreppable, even with a regex. I put it in, but he refused to use it because no other C++ compiler did that and it wouldn't be portable. I learned two things: 1. nobody wants local C++ extensions 2. the idea behind a keyword for cast was sound, and hence the D syntax which is easily greppableOr when someone again tries to figure proof and puts `else static assert(0)` at the end, making incremental porting impossible. (which i also wrote about in the last year: http://dpldocs.info/this-week-in-d/Blog.Posted_2023_02_20.html#static-assert-patterns-arguably harmful-for-porting )I'll have a look at that.C's ifdef is a big mess. D's version is a different big mess, but still a mess.A much smaller one. I have a lot of experience with the C one. It's often essentially impossible to tell which path through the tangle is being taken unless one dumps the preprocessor output and examines it. Even if one is faced with such a tangle in D, a strategic: ``` pragma(msg, "xyzzy"); ``` will let you know which branch is happening. I sometimes resort to that with templates.
Aug 24 2023
On Thursday, 24 August 2023 at 16:47:55 UTC, Walter Bright wrote:That was something different.No, it is a case of the same general problem. It even literally uses the `version` keyword in the language! The best advice I have is to never use any version specifier except the built in platform ones. `version(Windows)` is not great but not demonstrably harmful the way user-defined versions or `version(unittest)` and others influenced by build flags are just because those are *so* easy to get out of sync. And `version(unittest)` is actually extraordinarily dangerous because `unittest` is so generic. At least `version(no_fork)`, while it could be defined by multiple libraries in differing ways, is somewhat unlikely to be seen. But virtually all D code agrees on `unittest` being a condition they use since it is actually standardized. This would be mitigated if the -unittest switch took a module pattern. -version taking a module pattern would help limit its damage. You'd still want to match when building, but at least then it would be intentionally namespaced and avoid accidental conflicts.I wouldn't set it up that way. I'd version on IA64 within CRuntime_DigitalMars. I.e. overlapping version declarations are not a good plan, for precisely the reason you mention. Better to version along orthogonal axes.https://dlang.org/spec/version.html#predefined-versions Plenty of overlapping things in that existing list. (and i expect you meant X86_64 rather than IA64, but that falls apart if it ran on AArch64 too, so that'd be all the more reason to have a separately defined thing like Phobos did)It failed because none of them had ever ported code from 16 to 32 before, and so the future-proof solutions were all the wrong solutions.Indeed. Just like with D's `version` feature, it seemed like a good idea at the time, but didn't stand up to real world use in the long run.At some point, you're just going to have to run: grep -r version *The problem with this is that `-version` is global, including across dependencies of dependencies that might not even be in tree.
Aug 24 2023
On 8/24/2023 8:30 AM, Adam D Ruppe wrote:Or when someone again tries to figure proof and puts `else static assert(0)` at the end, making incremental porting impossible. (which i also wrote about in the last year: http://dpldocs.info/this-week-in-d/Blog.Posted_2023_02_20.html#static-assert-patterns-arguably harmful-for-porting )Not impossible, just initially a mite tedious. One can still incrementally port to AdrOS by adding: ``` version (Windows) { enum x = 7; } else version (AdrOS) { enum x = 0; // FIXME } else static assert(0); ``` and at least the compiler will tell you were all these places are. Then, a grep for FIXME will find every place you overlooked. You wrote that this is in hundreds of places in druntime. Great! If this technique wasn't used, I guarantee that any port to AdrOS will miss more than a few, and it will be hell to figure out what is going wrong. I guarantee it because it always happened when defaults were used for unknown systems. I prefer a bit of tedium to debugging hell. This is also why the `default` case is required for D switches.
Aug 24 2023
On Thursday, 24 August 2023 at 17:03:20 UTC, Walter Bright wrote:You wrote that this is in hundreds of places in druntime. Great! If this technique wasn't used, I guarantee that any port to AdrOS will miss more than a few, and it will be hell to figure out what is going wrong. I guarantee it because it always happened when defaults were used for unknown systems.I understand this point, but it doesn't take out what Adam said. Sometimes you're coding for a platform where DRuntime isn't ported to, without intention to port it in whole. I think we want to let the user to pick. By default, unimplemented DRuntime functions would cause a compilation failure, but that could be suppressed with `-version=Autostub`. Therefore, the versioning would be: ```D else version(Autostub){} else static assert(0, "Not implemented!"); ``` Alternatively for less verbose syntax, `object.d` would have ```D version(Autostub) enum autostubbing = true; else enum autostubbing = false; ``` ...making the versioning tail look like: ```D else static assert(autostubbing, "Not implemented!"); ```
Aug 24 2023
On Thursday, 24 August 2023 at 17:39:34 UTC, Dukc wrote:I think we want to let the user to pick.The top level static assert has no value. I went through with this let-the-user-pick thing in simpledisplay too, like described in the blog. It was a bunch of work that led nowhere; it is better to just leave the identifier undefined and let the test process catch it. These things look like a good idea when you're starting a new project, but they don't stand up to experience supporting things long term.Alternatively for less verbose syntax, `object.d` would havehahaha another example of how D's `version` keyword is useless baggage in the language, even for this trivial thing, you'd rather have an `enum`.
Aug 24 2023
There is an alternative solution which would be much simpler. If the scope for which a static assert is located, does not contribute towards symbols that get used and another is found, it won't fire. I.e. ```d void func() { static assert(false); } ``` As long as you don't call func, it won't fire. Same goes for: ```d module binding.b; version(Windows) { extern export void func(); } else { static assert(0, "Unimplemented"); } module binding.a; void func() { } ``` There would need to be a way to be stricter and go back to the way we have it now via cli. But as far as making porting easier? Yeah it can't be beat.
Aug 24 2023
On Thursday, 24 August 2023 at 20:51:26 UTC, Richard (Rikki) Andrew Cattermole wrote:If the scope for which a static assert is located, does not contribute towards symbols that get used and another is found, it won't fire.You can kinda achieve this with a zero arg template today: void func()() { static assert(0); // only triggered if actually called } I use this for a lot of things including optional dependencies too.There would need to be a way to be stricter and go back to the way we have it now via cli.`dmd -i main.d` would only trigger static assert failures in modules actually imported, so that can work for a while too, then `dmd *.d` to build all. But none of these offer compelling advantages over `grep -r "NotYetImplemented"`
Aug 24 2023
On 8/24/2023 1:51 PM, Richard (Rikki) Andrew Cattermole wrote:```d void func() { static assert(false); } ``` As long as you don't call func, it won't fire.The static assert will fail at compile time. `assert(0)` will fail at run time.
Aug 24 2023
On 25/08/2023 9:26 AM, Walter Bright wrote:On 8/24/2023 1:51 PM, Richard (Rikki) Andrew Cattermole wrote:Currently yes. I proposed a change where it requires some form of usage or would have contributed towards some usage. Basically a deferment of failing.```d void func() { static assert(false); } ``` As long as you don't call func, it won't fire.The static assert will fail at compile time. `assert(0)` will fail at run time.
Aug 24 2023
On Thursday, 24 August 2023 at 17:03:20 UTC, Walter Bright wrote:Not impossible, just initially a mite tedious. One can still incrementally port to AdrOS by adding: ``` version (Windows) { enum x = 7; } else version (AdrOS) { enum x = 0; // FIXME } else static assert(0); ```So your solution to how static assert(0) sucks is to literally add a branch that doesn't use it. If this isn't a full concession, I don't know what is! But, I'd point out that this kind of thing is even worse than not having the `else static assert(0)` branch at all. Suppose you wrote it like this in the first place: ``` version(Windows) enum x = 8; version(linux) enum x = 8; // somewhere else version(Windows) enum y = 9; version(linux) enum y = 7; ``` You port it to AdrOS. ``` import that; void main() { auto thing = x; } ``` You now get a nice error: undefined identifier "x". *as you use it*, the compiler tells you it is missing and you know to go fix it. Then later, when you use y, you again get undefined identifier and you go back and add it. It is incrementally ported - always compiling the core, with more advanced test cases failing until you fill in those features. Now, contrast to the static assert pattern: ``` version(Windows) enum x = 8; else version(linux) enum x = 8; else static assert(0); // somewhere else version(Windows) enum y = 6; else version(linux) enum y = 7; else static assert(0); ``` You port it to AdrOS. ``` import that; void main() { auto thing = x; } ``` You get compile errors before you even get into main! You have to do *all* the work up front to test *any* of it. So, we do the tedious thing you suggested: ``` version(Windows) enum x = 8; else version(linux) enum x = 8; else version(AdrOS) enum x = 9; else static assert(0); // somewhere else version(Windows) enum y = 6; else version(linux) enum y = 7; else version(AdrOS) enum y = 0; // FIXME else static assert(0); ``` After who knows how many hours, you've finished all that tedious nonsense and can finally compile your test program again: ``` import that; void main() { auto thing = x; } ``` Yay, hello world finally works! Then a month later, you try to use `y`, forgetting that there's a FIXME. The program fails in mysterious ways, throwing some invalid flag exception or something, but the docs say y is a valid flag. A few hours later, you try to `printf("%d", y)` and you see a 0. Oh yeah, you never ported that. Time to grep for FIXME again. Complete waste of time with zero help from the compiler. It is better to never use static assert(0) at all like this. It is good inside a template to validate arguments that the user might fix and try recompiling again. It is not good at module level where it just wastes time.I guarantee it because it always happened when defaults were used for unknown systems.I'm not suggesting using defaults for unknown systems.I prefer a bit of tedium to debugging hell.I prefer neither.
Aug 24 2023
On 8/24/2023 11:04 AM, Adam D Ruppe wrote:But, I'd point out that this kind of thing is even worse than not having the `else static assert(0)` branch at all. Suppose you wrote it like this in the first place: ``` version(Windows) enum x = 8; version(linux) enum x = 8; // somewhere else version(Windows) enum y = 9; version(linux) enum y = 7; ``` You port it to AdrOS. ``` import that; void main() { auto thing = x; } ``` You now get a nice error: undefined identifier "x". *as you use it*, the compiler tells you it is missing and you know to go fix it. Then later, when you use y, you again get undefined identifier and you go back and add it. It is incrementally ported - always compiling the core, with more advanced test cases failing until you fill in those features.If you want to do it that way, that's ok. I just want to head off the commonplace use of defaults. I also like, when I've ported to a new OS, knowing where all the OS specific tweaks need to go, up front. Getting an "undefined x" doesn't tell me where the x needs to be defined. But your method is still workable.
Aug 24 2023
On Thursday, 24 August 2023 at 17:03:20 UTC, Walter Bright wrote:On 8/24/2023 8:30 AM, Adam D Ruppe wrote:For import or declaration, I refer to use version (Windows) { enum x = 7; } else pragma(msg, "Unsupport system for " ~ __MODULE__); For function logic, still refer to use static assert int foo() { version (Windows) { return 7; } else static assert(0, "Unsupport system for " ~ __FUNCTION__); }Or when someone again tries to figure proof and puts `else static assert(0)` at the end, making incremental porting impossible. (which i also wrote about in the last year: http://dpldocs.info/this-week-in-d/Blog.Posted_2023_02_20.html#static-assert-patterns-arguably harmful-for-porting )Not impossible, just initially a mite tedious. One can still incrementally port to AdrOS by adding: ``` version (Windows) { enum x = 7; } else version (AdrOS) { enum x = 0; // FIXME } else static assert(0); ```
Aug 24 2023
On Thursday, 24 August 2023 at 20:29:38 UTC, An Pham wrote:For function logic, still refer to use static assert int foo()This is still going to stop compilation so it doesn't scale very well. I've taken to `throw new NotYetImplementedException();` instead since then you can build.
Aug 24 2023
On 8/24/2023 8:30 AM, Adam D Ruppe wrote:I wrote at greater length about this here: http://dpldocs.info/this-week-in-d/Blog.Posted_2022_11_14.html#redesign-for-template-emission-woesUnfortunately, when compiling a library with one set of switches and linking it with a binary created with another set of switches, one is always going to run the risk of mismatch (and not just with -version). For a library designed to be distributed as a binary, the interface to it needs to be carefully designed to minimize this risk. For a library distributed as source, but intended to be built separately, it's less of an issue but it still needs to be designed to minimize the risk. This just comes with static compilation. A header-only library doesn't have these problems.
Aug 24 2023
On Wednesday, 23 August 2023 at 17:24:24 UTC, Adam D Ruppe wrote:We have the ability to declare information (`enum`s and so on) & branch on it at compile time (`static if`), we have a package & module system to handle who can and does read that data. Beyond that, it's the author & build system’s problem imo. I get the problem that `version` tries to solve, but I reckon if it didn’t already exist then it wouldn’t get added now.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.
Aug 24 2023
On Thursday, 24 August 2023 at 16:26:10 UTC, John Colvin wrote:We have the ability to declare information (`enum`s and so on) & branch on it at compile time (`static if`), we have a package & module system to handle who can and does read that data. Beyond that, it's the author & build system’s problem imo.Yeah, it would be kinda interesting if `core.build_environment` just predefined some enums and `version` was eliminated. Would be a simplification of the language and enable new things since you could maybe reflect over it! lol
Aug 24 2023
On 8/24/2023 9:26 AM, John Colvin wrote:We have the ability to declare information (`enum`s and so on) & branch on it at compile time (`static if`), we have a package & module system to handle who can and does read that data. Beyond that, it's the author & build system’s problem imo. I get the problem that `version` tries to solve, but I reckon if it didn’t already exist then it wouldn’t get added now.At one point, druntime was largely converted to using enums and static if, to implement version algebra. It inevitably increased in complexity until nobody understood what was happening (due to circular imports). It got dumped in my lap. I converted it all back to versions, and it's been trouble free ever since. People can do as they like, but I prefer to stick with stupid simple version :-)
Aug 24 2023
On Thursday, 24 August 2023 at 21:37:54 UTC, Walter Bright wrote:At one point, druntime was largely converted to using enums and static if, to implement version algebra. It inevitably increased in complexity until nobody understood what was happening (due to circular imports). It got dumped in my lap. I converted it all back to versions,When was this?and it's been trouble free ever since.It also tends to lag adoption on new platforms by a significant degree.
Aug 24 2023
On 8/24/2023 2:51 PM, Adam D Ruppe wrote:On Thursday, 24 August 2023 at 21:37:54 UTC, Walter Bright wrote:Beats me. Long time ago. I think stdc.stdio was one of the afflicted, so you can check the history on that.At one point, druntime was largely converted to using enums and static if, to implement version algebra. It inevitably increased in complexity until nobody understood what was happening (due to circular imports). It got dumped in my lap. I converted it all back to versions,When was this?Having done ports to new OS's myself, that hasn't been a significant impediment.and it's been trouble free ever since.It also tends to lag adoption on new platforms by a significant degree.
Aug 24 2023
On Monday, August 21, 2023 12:46:47 PM MDT Basile B. via Digitalmars-d wrote:On Monday, 21 August 2023 at 18:01:20 UTC, Walter Bright wrote:The way that version statements try to enforce a lack of boolean logic reflects the way that Walter believes (rightly or wrongly) that versioning code should work based on his experience, but it does not actually prevent you from writing code that uses boolean logic for versioning. It just makes it harder, and given how much most programmers hate code duplication (whereas the way that Walter wants versioning done typically introduces quite a lot of code duplication, even if it's for a good cause), plenty of D programmers routinely work around how version works in order to introduce boolean logic into how they version code. D runtime is not immune to this any more than any other D code, particularly since Walter wrote very little of it. If anything, it probably follows what Walter wants more than most D code, because it's official D stuff, but the way that Walter wants versioning done (while arguably a good idea) strongly goes against how programmers typically function, so it's pretty natural that stuff like this would creep into druntime. Arguably, stuff like this should be removed from druntime to reflect how Walter wants versioning to be done, but clearly, no one who has spent much time working on druntime has felt strongly enough about it to spend the time to fix the problem, and whoever reviewed the code in question when it was first introduced didn't insist on it being fixed. Utimately, the fact that people routinely try to work around how version statements work in D doesn't really say anything about whether the reasons for them working the way that they do are good or not. It just shows that (for better or worse) most programmers haven't been brought around to Walter's way of thinking with regards to versioning. In reality, I expect that most of the programmers versioning code in D aren't even aware of why it works the way that it works. They just want to version code the way that comes naturally to them, so they end up fighting how version works rather than adjusting how they function to match. Personally, I think that Walter is probably right about how versioning should be done, but at the same time, I find it irritating, because it introduces so much code duplication. The code duplication is there to reduce the complexity and reduce the risk of other kinds of problems that typically come with having different versions of the same code, so it is arguably a good a tradeoff, but it often just feels wrong even if it isn't. 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. - Jonathan M DavisOn 8/21/2023 9:26 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. 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.On Monday, 21 August 2023 at 16:19:25 UTC, Walter Bright wrote:That's why I wouldn't use the DEFAULTS approach. Just make a table.On 8/21/2023 8:39 AM, Basile B. wrote:you have to read top down to see what DEFAULTS do.Like you pretend that `enum DEFAULTS` is not a workaround the limitations.I would not use the DEFAULTS, but would spell it out for each system. Then it looks like a table, with rows and columns, and makes it easy to see which setting is for which system.
Aug 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.
Aug 21 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
Aug 22 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.pdfI didn't know about this paper, well done!
Aug 23 2023
On Tuesday, 22 August 2023 at 17:57:52 UTC, 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.pdfLove this part: ..."Many of the problems we discuss stem from the 'never mind good,we want it nextweek' approachto software"... I thought it was talking or related where I work until I read the year 1992 on the footnote. =] Matheus.
Aug 28 2023
On Monday, 21 August 2023 at 16:19:25 UTC, Walter Bright wrote:On 8/21/2023 8:39 AM, Basile B. wrote:I've been informed that you are not actually perfectly satisfied with D versions and that you personally use workarounds, for example here: https://github.com/dlang/dmd/commit/80a62817f13a79d2d2d84cdb46697a2c1406a212Like you pretend that `enum DEFAULTS` is not a workaround the limitations.I would not use the DEFAULTS, but would spell it out for each system. Then it looks like a table, with rows and columns, and makes it easy to see which setting is for which system.
Aug 24 2023
On 8/24/2023 7:37 AM, Basile B. wrote:I've been informed that you are not actually perfectly satisfied with D versions and that you personally use workarounds, for example here: https://github.com/dlang/dmd/commit/80a62817f13a79d2d2d84cdb46697a2c1406a212I'm always experimenting with better ways to do things.
Aug 24 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: ```D enum STUFF = static switch(__OS__) { case OS.Linux: break; default: break; }; ``` In a sane language that would look like: ```D 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
Aug 22 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 quoWalter 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
Aug 23 2023
On 8/23/2023 5:37 AM, Nick Treleaven wrote: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.comYes, and it's paused for the moment as we're working on fixing issues rather than adding new features.
Aug 23 2023
On 8/22/2023 3:03 PM, ryuukk_ wrote:Since i got confused with ``version(linux)`` and ``version(Linux)``For those who wonder why "linux", it is to match C's predefined identifier that serves the same purpose.
Aug 23 2023
On Tuesday, 22 August 2023 at 22:03:41 UTC, ryuukk_ wrote: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: ```D enum STUFF = static switch(__OS__) { case OS.Linux: break; default: break; }; ```Like this? ``` enum OS { FreeBSD, Linux, NetBSD, Windows } OS getOs() { version(FreeBSD)return OS.FreeBSD; version(linux)return OS.Linux; version(NetBSD)return OS.NetBSD; version(Windows)return OS.Windows; assert(false,"unsupported OS"); } ```
Aug 27 2023