February 11, 2009
"Walter Bright" wrote
> Nick Sabalausky wrote:
>> "Walter Bright" <newshound1@digitalmars.com> wrote in message news:gmt6l0$rff$1@digitalmars.com...
>>> Denis Koroskin wrote:
>>>> Does it look any better? No way!
>>> Of course doing it that way doesn't look any better, because it still just replicates the C preprocessor style of doing it.
>>>
>>
>> Which just goes to show that the restrictions you've placed on D's version() (in order to eliminate rat's nest versioning) DON'T eliminate rat's nest versioning.
>
> But they do make it more painful to write the rat's nest, which can be motivating to find a more appropriate solution.

And when the rats nest MUST be created?  Why make it more painful?  I'm sure if Hans Bohem could write it in a clearer fashion he would have.

To be perfectly clear, I absolutely agree with version statements not messing with the separation between semantic analysis and parsing, but these shortcuts for having multiple version identifiers in one statement do not hurt anything.  I contend it is the lack of separation in C's preprocessor that makes it difficult to understand, not the lack of requirements on the #if itself.  At least, that's what I've always hated about C/C++ preprocessor.

The same arguments could be said about actual if statements in code, yet you have no qualms not forcing people to combine their logic into boolean variables before using an if statement.

>>> A far better solution...
>>
>> And we can come up with better solutions for C as well. Granted, the optimal D solution is going to be much better than the optimal C solution, but it won't be due to version()'s lack of !, ||, &&, etc...
>
> When cookies and veggies are laid out on the buffet, I tend to reach for the cookies <g>.

It's more like moldy cookies and half-eaten donuts :)  Neither looks appetizing, but when your really hungry...

-Steve


February 11, 2009
Jason House escribió:
> Walter Bright Wrote:
> 
>    
>> 5. Why can't one 'version out' syntax that is not recognized by the compiler?
>>
>> The problem is that supporting this requires semantic analysis in order to successfully lex and parse the source code. Breaking this will make the lexing and parsing an order of magnitude harder for third party tools to do. If you need to 'comment out' a section of syntactically invalid code, use the /+ ... +/ nesting comment.
> 
> Would you be willing to introduce an alternative to /+ +/ which would be treated differently by the D1 and D2 compilers? Here are some examples with no attempt at creativity:
> beginD1 endD1
> D1 D1 (works like string delimiters)
> /D2 D2/

Why not have special versions like D1, D2, D3 for the compiler to treat them differently? If a version(DX) is found that is not the current's compiler version, it just consumes tokens (counting opening and closing of brackets, etc.) until the curly's close. Much like the asm statement.
February 11, 2009
Steven Schveighoffer wrote:
> "Walter Bright" wrote
>> Denis Koroskin wrote:
>>> Does it look any better? No way!
>> Of course doing it that way doesn't look any better, because it still just replicates the C preprocessor style of doing it.
>>
>> A far better solution is to create a series of modules:
>>
>> gcnetbsd.d
>> gchurd.d
>> gcsunos5.d
>> ...
>>
>> and inside each one put the specifics for that particular system. The huge advantage of this is that if I want to create a BrightBSD operating system, I just have to write a:
>>
>> gcbrightbsd.d
> 
> All you have done is split the mess into separate files.  This does not solve the problem.

How does it not solve the problem?

> 
>> rather than trying to carefully fold it into that conditional compilation mess without inadvertently breaking other platform support. (And I cannot even tell if I broke the SunOS5 platform support or not, because I don't have a SunOS5 platform to test it on.).
> 
> But you have, because inadvertently, you changed some code in the actual implementation to use the new identifiers you made in your special new file. Now you have to go back and rethink the sunos include because you broke it. Mess still exists.  (of course, I have no idea, but I gave you as much of an example/proof as you did ;)

See my example of O_APPEND for the proof that it does solve the problem without breaking other platforms.
February 11, 2009
Hello Don,

> bearophile wrote:
> 
>> Don:
>> 
>>> I requested module-scope static if, and Walter said "that's probably
>>> almost impossible to implement", but he did it anyway <g>.
>>> 
>> Walter does almost the impossible, sometimes. I'll have to remember
>> this :-)
>> 
>> Bye,
>> bearophile
> The traditional way to get an engineer to do something, is to tell him
> you believe it's impossible. Works very well with Walter.
> Also Walter tends not to promise to do something, unless he's already
> finished it <g>.


So I assume it must follow that engineers should never be taught computational complexity theory... determining that a problem is intractable might affect their attempts at ingenious solutions that shouldn't exist. :D


-JJR


February 11, 2009
"Andrei Alexandrescu" wrote
> Steven Schveighoffer wrote:
>> "Walter Bright" wrote
>> Think of code that is versioned around architecture that would look
>> horrendous if you have to do version statements that have all different
>> combinations of stuff.  If I have 5 different architectures to support, I
>> don't want to have to define a module that has 2^5 different version
>> combinations.  On top of that, the way versioning works, you need 2^4 * 5
>> different statements:
>>
>> version(A)
>> {
>>    version = AorB;
>>    version = AorC;
>>    version = AorD;
>>    version = AorE;
>>    version = AorBorC;
>>    version = AorBorD;
>>    // ad nauseum.
>> }
>> version(B)
>> {
>>    // oh fun! let's do it again!!!!
>>    version = AorB;
>>    version = BorC;
>>    version = BorD;
>>    ...
>> }
>>
>> When I add another architecture, *gasp* I have to double the statements (to do them now with and without version(F) ), and now I have to do another 2^5 statements for the version(F) block.  Wheee!
>
> But this is clearly the wrong way of cutting the pie. What you need is define features that are supported by some of the versions. You don't need to judge in terms of logical operators between versions.
>
> version (A) version = canBeUsedToPickupChicks;
> version (C) version = canBeUsedToPickupChicks;
> // version (B) no good
>

Sometimes, coming up with a better identifier than this || that is not easy.

For example, in Tango, there is a version for Posix, which is defined if linux or darwin or bsd is defined, but we sometimes get people arguing that Windows is posix compliant too!

Not only that, but are you going to want to define a special identifier in the header file that is used exactly ONCE in your code?  Often times, I see this in source files:

version(linux)
  version = specialWay;
version(darwin)
  version = specialWay;

Because they need that combination of linux || darwin in one spot, but nowhere else.  I see no reason why this is more readable than

version(linux || darwin)

in that one spot.  I do not have to go looking for specialWay in this file, or some other header file, or simply have to read through useless repetitive statements.  It's perfectly clear what it means.  How does this promote confusing versioning?  Many people have pointed out the possibility of spelling mistakes now more likely because you have to repetitively specify the same version under other version branches.

I see allowing version(x || y) as something that has absolutely no drawbacks.  None of the arguments against it have had any merit, because the examples of how bad it would be are not bad because of the existance of that construct.  They are simply complicated examples of versioned code, which would probably benefit from allowing ||.

It would even make code more maintainable in your example:

version(A || C) version = canBeUsedToPickUpChicks;

Note no duplication of the long token you chose ;)

-Steve


February 11, 2009
"Walter Bright" <newshound1@digitalmars.com> wrote in message news:gmt8t7$1042$1@digitalmars.com...
> Derek Parnell wrote:
>> On Tue, 10 Feb 2009 14:01:29 -0800, Walter Bright wrote:
>>
>> My apology. The problem is more than run-time performance issues. The
>> more
>> pressing problem, IMHO, is the one of maintenance costs. Duplicated code
>> is
>> a significant cost burden. Not only may each duplication require
>> updating,
>> but there is extra effort in analyizing every duplicate to see if it
>> *does*
>> need updating. And every act of updating increases the opportunity for
>> introducing bugs.
>
> Let's take an example, like the enum for O_XXXX in std.c.linux.linux. Some of those values are common between platforms, and some are unique to particular platforms. So you might be tempted to write:
>
{snip}

I have to admit, that's a very compelling example. I hadn't thought of anything like that. I guess the moral is "DRY is normally great, but be very careful with it when using it across multiple builds." But I'm still not sure we should be going that far. For instance:

enum
{
    version(Linux || OSX)
    {
        O_RDONLY = 0,
        O_WRONLY = 1,
        O_RDWR = 2,
        O_CREAT = 0100,
    }
    version(Linux)
    {
        O_APPEND = 02000,
    }
    version(OSX)
    {
        O_APPEND = 8,
        O_SYMLINK = 0x200000,
    }
}

That seems reasonable to me. Granted, with a small enum like that, completely replicating the whole enum would be fine, but imagine if it were something much larger, like MS or Apple error codes (though that may not be an ideal example since they're platform-specific).

Unlike Walter's example, this method clearly allows for DRY, would would be quite an improvement on large enums. But I would argue that this method also provides for just as much robustness:

Scenario 1: Porting to Windows, BSD, or Joe's Embedded OS:
Walter's example results in an obvious compile-time failure at the enum.
Mine might not do that (but maybe it *could* still do some sort of
"version(Linux||OSX) {...} else fail" ), however mine does still cause an
error anytime there's an attempt to call (or maybe even define) the "file
open" function (Since there's no valid value for the paramater. Even the
default value would be invalid).

Scenario 2: Linux User Joe wants to add an "O_PREPEND" (Yea, I know that's
*very* contrived):
He's going to see three sections, Linux, Mac, and Both. The "Linux||OSX"
part makes it clear to him that he should double-check the value of
O_PREPEND on OSX if he wants to stick it in "Both", or else he knows he can
just leave it in the Linux-only part or check with the project's manager.
Later on, if Project Manager Suzy uses both systems and knows that O_PREPEND
is the same on both, she can just move it to the "Both" section.

Besides, without expression-level version(), the project's author might just as likely think "Shoot, this is stupid that D doesn't let me make this nicely DRY. But I really like DRY, so I guess I'll just resort to an external C-like pre-processor".


February 11, 2009
"Walter Bright"  wrote
> Steven Schveighoffer wrote:
>> "Walter Bright" wrote
>>> Denis Koroskin wrote:
>>>> Does it look any better? No way!
>>> Of course doing it that way doesn't look any better, because it still just replicates the C preprocessor style of doing it.
>>>
>>> A far better solution is to create a series of modules:
>>>
>>> gcnetbsd.d
>>> gchurd.d
>>> gcsunos5.d
>>> ...
>>>
>>> and inside each one put the specifics for that particular system. The huge advantage of this is that if I want to create a BrightBSD operating system, I just have to write a:
>>>
>>> gcbrightbsd.d
>>
>> All you have done is split the mess into separate files.  This does not solve the problem.
>
> How does it not solve the problem?

I assume that the rats nest is necessary for the code to function. Splitting up the rats nest into either a) mostly duplicated rats nests, or b) individual chunks of rats nest still constitutes a rat's nest.

>>> rather than trying to carefully fold it into that conditional compilation mess without inadvertently breaking other platform support. (And I cannot even tell if I broke the SunOS5 platform support or not, because I don't have a SunOS5 platform to test it on.).
>>
>> But you have, because inadvertently, you changed some code in the actual implementation to use the new identifiers you made in your special new file. Now you have to go back and rethink the sunos include because you broke it. Mess still exists.  (of course, I have no idea, but I gave you as much of an example/proof as you did ;)
>
> See my example of O_APPEND for the proof that it does solve the problem without breaking other platforms.

That example has nothing to do with || being allowed, and nothing to do with your exhibit B.  Your simple example makes sense to split into multiple files, but you simply waved your magic "split into multiple files" wand at Hans' code and because of the simplicity of D's versioning system, it's more maintainable?  I find it hard to believe without proof.

Besides, your example exists *without* any new features to the versioning system.  That is, your example just shows how it's easier to maintain if you put the version identifier at the outermost layer, instead of in the innermost layer, and I agree with you there.  I just want to try and get rid of the cruft of:

version(A)
   version = AorB;
version(B)
   version = AorB;

If you want to prove how much easier it is, actually split Hans' rat's nest into multiple files how you would do it, and I'll show you that it isn't any harder to maintain if you use ||.  I think Nick already showed how much more readable it is in one file (although he did also use ! in addition to || and &&).

-Steve


February 11, 2009
On Tue, Feb 10, 2009 at 8:50 PM, Walter Bright <newshound1@digitalmars.com> wrote:

> I'll argue that I've never seen anyone create such a mess in D, while I see it regularly in C. So something about D is discouraging developing those things.

D is also only _really truly_ supported on about three or four platforms, has been around for a fraction of the time C has, and has a core code-producing community of I'd say around 100~150 people. Sample bias.

> I think the tipping point is that it's too easy in C to slip into writing such a mess without actually trying to, while in D you have to work harder to do it. Hard enough that one might as well do it better in the first place.

If nothing - NOTHING - else, please give us version(!something).  Out of everything wrong with it, that is the worst.  You say that according to "cognitive studies" that people don't see the !; then why didn't you remove it from the rest of the language?  And how is THIS:

version(Poopy) {} else
{

}

supposedly better?!
February 11, 2009
John Reimer wrote:
> So I assume it must follow that engineers should never be taught computational complexity theory... determining that a problem is intractable might affect their attempts at ingenious solutions that shouldn't exist. :D

I've suspected that's why breakthroughs are made by the young, as they don't yet understand what's impossible.
February 11, 2009

grauzone wrote:
> I see your points, although I don't quite agree. Programmers will continue to do stupid things, even if it hurts. And in these cases, having an "intentionally hard to use" version-statement might increase the overall mess even further.
> 
> But what do you say about the possibility of uncatched typos? (the
> "version(linxu) {" example)

Versions are not boolean flags that are either true or false [1]. They're zero-state flags that either exist or don't.  You can't spell-check something which simply may not exist.

So let's say you have to define all version identifiers.  The problem with this is that the more you use versioning, the more hideous your compile-lines are going to become...

dmd foo --version=UseMMX=false --version=UseSSE=false --version=UseSSE2=true --version=Use3DNow=false --version=UseSSE2_AMD=false --version=UseSSE3=false --version=UseSSE4=false --version=UseSPARCExtension=false --version=UseAltivec=false --version=UseARMStuff=false --version=OhGodMakeItStop=false

As opposed to

dmd foo --version=UseSSE2

You might be able to rework the syntax so that you can define the default value of a version symbol in a module, then override it from the command line.

module simd_versions;
version UseMMX=false, UseSSE=false, UseSSE2=false, ...;

  -- Daniel


[1] I'm ignoring integer versions which have a similar problem, but would just make this post longer :P