October 04, 2009
To test my level of ignorance of D2, and to test how structs can be used to implement complex numbers in the std lib I have done few experiments with something simile, subsets of integers.

I have found some problems, some of them may come from my ignorance. This is a natural number:

import std.stdio: writeln;

struct Natural {
    int x = 1;
    alias x this;

    invariant() {
        assert(x >= 1);
    }
}

void main() {
    Natural x, y;
    x = 10;
    y = -20; // OK, invariant() isn't called because it's a direct access

    // From the D2 docs: http://www.digitalmars.com/d/2.0/class.html#Invariant
    // The invariant can be checked when a class object is the argument
    // to an assert() expression, as:
    assert(x); // test.d(20): Error: expression x of type Natural does not have a boolean value
}

It seems invariant() can be used in structs too (even if in D2 docs they are named class invariants), but I think the assert(x) doesn't work.


To solve the problem of the invariant not being called in the assignment I have written this second version:

import std.stdio: writeln;
import std.conv: to;

struct Natural {
    int x_ = 1;

    int x() { return this.x_; }
    int x(int xx) { this.x_ = xx; return xx; }
    alias x this;

    invariant() { assert(this.x_ >= 1, "not a natural"); }

    string toString() { return to!string(this.x_); }
}

void main() {
    Natural x, y;
    x = 10;
    writeln(x, " ", x + x * 3); // OK

    // a problem: the error message gives the line number of the
    // assert instead of the assignment
    y = -20; // core.exception.AssertError@test2.d(11): not a natural
}

Now it works better, but the assert gives an unhelpful line number.



This is a variant, a ranged value:

import std.stdio: writeln;
import std.conv: to;

struct Ranged(int RANGED_MIN, int RANGED_MAX) {
    int x_ = RANGED_MIN;

    int x() { return this.x_; }
    int x(int xx) { this.x_ = xx; return xx; }
    alias x this;

    invariant() {
        //assert(this.x_ >= RANGED_MIN, "Ranged value too much small");
        assert(this.x_ < RANGED_MAX, "Ranged value too much big");
    }

    string toString() { return to!string(this.x_); }
}

void main() {
    typedef Ranged!(10, 20) ranged;
    ranged x;
    writeln(x);
    ranged y = 1000; // Uh, bypasses the setter, no errors
    writeln(y); // 0?
}


I have commented out the first assert to understand better what's happening.
In the line:
ranged y = 1000;
The invariant isn't called, the value 1000 goes nowhere, and even the int x_ = RANGED_MIN; is being bypassed, so in this.x_ goes a zero.
This will be another problem for library-defined complex numbers. So in the end I may like to keep complex numbers in the language for now.

Bye,
bearophile
October 05, 2009
Justin Johansson wrote:
> Andrei Alexandrescu Wrote:
> 
>> Nick Sabalausky wrote:
>>> "Justin Johansson" <no@spam.com> wrote in message news:haavf1$2gs7$1@digitalmars.com...
>>>> It's a difficult challenge to get high performance, readable and maintainable code out of complex number
>>>> intensive algorithms.   Use of library types for complex numbers has, in my experience been problematic.
>>>> Complex numbers should be first class value types I say.
>>>>
>>> There's been discussion before (I can't find it now, or remember the name for it) of type systems that allow for proper handling of things like m/s vs. m/(s*s) vs inch/min etc. I haven't actually worked with such a feature or with complex/imaginary numbers in any actual code, so I can't be sure, but I've been wondering if a type system like that would be an appropriate (or even ideal) way to handle real/complex/imaginary numbers.
>> It better be. Complex numbers aren't that complicated of a notion. What's lost in pulling them out of the language is the ability to define literals.
> 
>> "Now please name five remarkable complex literals."
> 
> (re, im) ::= (0, 0), (1,0), (0,1), (1,1), (pi/2, 0), (0, pi/2), e_to_the_power_(minus j),
>  e_to_the_power_(minus j * pi/2)
> 
> Is that what you mean?

(Three of those are real.)

What I meant was that complex literals helped by syntax are seldom likely to improve code quality. Many numeric literals are of questionable taste anyway and should at best defined as symbols. I don't see why complex literals shouldn't follow the same recommendation.


Andrei
October 05, 2009
"Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:hab3r2$2pgr$1@digitalmars.com...
> Nick Sabalausky wrote:
>>
>> There's been discussion before (I can't find it now, or remember the name for it) of type systems that allow for proper handling of things like m/s vs. m/(s*s) vs inch/min etc. I haven't actually worked with such a feature or with complex/imaginary numbers in any actual code, so I can't be sure, but I've been wondering if a type system like that would be an appropriate (or even ideal) way to handle real/complex/imaginary numbers.
>
> It better be. Complex numbers aren't that complicated of a notion. What's lost in pulling them out of the language is the ability to define literals. Now please name five remarkable complex literals.
>
> The feature you're referring to is called dimensional analysis.
>
>> (I've also been wondering if it might be a huge benefit for distinguishing between strings that represent a filename vs file content vs file-extention-only vs relative-path+filename, vs absolute-path-only, etc. I've been really wanting a better way to handle that than just a variable naming convention.)
>
> I don't quite think so. In fact I don't think so at all. Pathnames of various flavors evolve quite a bit in many programs, and having to worry about tracking their type throughout is too much aggravation to be worthwhile. Last thing I'd want when manipulating pathnames would be a sticker of a library slapping my wrist anytime I misuse one of its six dedicated types.
>

Last thing I'd want when dealing with paths and filenames would be making one small mistake and getting *neither* a runtime nor a compile-time error, and then have the program happily trounce about the filesystem with corrupted names or paths (sort of like getting m/s and m/(s*s) mixed up). Sure, it *might* error out with an "access denied" or "file not found" or something like that before actually doing anything wrong, but that's far from guaranteed. And even if it doesn't, it might not cause real harm, but there are times when it could so why risk it?

I guess I just don't see why something like (fig. A) would be bad if (fig. B) and (fig. C) are considered good (as I certainly consider them):

-- fig A -----------
open(inFile ~ "out" ~ inFile.file ~ "log");
// As many as 4 different errors that could be caught but currently aren't.
// (But obviously all would be overridable, of course)
// Without such checking, if inFile contained "/usr/home/joe/foo/bar.dat",
// the result would be:
//   "/usr/home/joe/foo/bar.datoutbar.datlog"

// Meant to do this:
open(inFile.path ~ "out/" ~ inFile.name ~ ".log");
// Result now: "/usr/home/joe/foo/out/bar.log"
---------------------

-- fig B -----------
int add(int* a, int* b)
{
    return a + b; // Error currently caught
    // Meant:
    return *a + *b;
}
---------------------

-- fig C -----------
auto accel = velocity * time; // Error caught with that "dimensional
analysis"
// Meant this:
auto accel = velocity / time;
---------------------

Granted, I agree that as things currently are, it might not be possible to write a library to handle such file/path manipulations without it being too unweildy for the library's prospective users. But with a good "dimensional analysis" system...maybe not?


October 05, 2009
"Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:hac2ku$1nfu$1@digitalmars.com...
> Justin Johansson wrote:
>>
>>> "Now please name five remarkable complex literals."
>>
>> (re, im) ::= (0, 0), (1,0), (0,1), (1,1), (pi/2, 0), (0, pi/2),
>> e_to_the_power_(minus j),
>>  e_to_the_power_(minus j * pi/2)
>>
>> Is that what you mean?
>
> (Three of those are real.)
>
> What I meant was that complex literals helped by syntax are seldom likely to improve code quality. Many numeric literals are of questionable taste anyway and should at best defined as symbols. I don't see why complex literals shouldn't follow the same recommendation.
>

I think people just don't like the idea of having to deal with a distinction of "Some types can have nice handy literals but others can't."


October 05, 2009
Nick Sabalausky wrote:
> "Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:hab3r2$2pgr$1@digitalmars.com...
>> Nick Sabalausky wrote:
>>> There's been discussion before (I can't find it now, or remember the name for it) of type systems that allow for proper handling of things like m/s vs. m/(s*s) vs inch/min etc. I haven't actually worked with such a feature or with complex/imaginary numbers in any actual code, so I can't be sure, but I've been wondering if a type system like that would be an appropriate (or even ideal) way to handle real/complex/imaginary numbers.
>> It better be. Complex numbers aren't that complicated of a notion. What's lost in pulling them out of the language is the ability to define literals. Now please name five remarkable complex literals.
>>
>> The feature you're referring to is called dimensional analysis.
>>
>>> (I've also been wondering if it might be a huge benefit for distinguishing between strings that represent a filename vs file content vs file-extention-only vs relative-path+filename, vs absolute-path-only, etc. I've been really wanting a better way to handle that than just a variable naming convention.)
>> I don't quite think so. In fact I don't think so at all. Pathnames of various flavors evolve quite a bit in many programs, and having to worry about tracking their type throughout is too much aggravation to be worthwhile. Last thing I'd want when manipulating pathnames would be a sticker of a library slapping my wrist anytime I misuse one of its six dedicated types.
>>
> 
> Last thing I'd want when dealing with paths and filenames would be making one small mistake and getting *neither* a runtime nor a compile-time error, and then have the program happily trounce about the filesystem with corrupted names or paths (sort of like getting m/s and m/(s*s) mixed up). Sure, it *might* error out with an "access denied" or "file not found" or something like that before actually doing anything wrong, but that's far from guaranteed. And even if it doesn't, it might not cause real harm, but there are times when it could so why risk it?

I think it's a judgment call. I did see a few examples throughout the years that created special types for various pathname components. I think one early C++ book had such an example. None of those attempts survived, and perhaps that could mean something. What may be happening is that in applications, the same variable may be e.g. a filename or a dirname, and handling that with an algebraic type may be considered too much aggravation.

> I guess I just don't see why something like (fig. A) would be bad if (fig. B) and (fig. C) are considered good (as I certainly consider them):
> 
> -- fig A -----------
> open(inFile ~ "out" ~ inFile.file ~ "log");
> // As many as 4 different errors that could be caught but currently aren't.
> // (But obviously all would be overridable, of course)
> // Without such checking, if inFile contained "/usr/home/joe/foo/bar.dat",
> // the result would be:
> //   "/usr/home/joe/foo/bar.datoutbar.datlog"
> 
> // Meant to do this:
> open(inFile.path ~ "out/" ~ inFile.name ~ ".log");
> // Result now: "/usr/home/joe/foo/out/bar.log"
> ---------------------
> 
> -- fig B -----------
> int add(int* a, int* b)
> {
>     return a + b; // Error currently caught
>     // Meant:
>     return *a + *b;
> }
> ---------------------
> 
> -- fig C -----------
> auto accel = velocity * time; // Error caught with that "dimensional analysis"
> // Meant this:
> auto accel = velocity / time;
> ---------------------
> 
> Granted, I agree that as things currently are, it might not be possible to write a library to handle such file/path manipulations without it being too unweildy for the library's prospective users. But with a good "dimensional analysis" system...maybe not?

Well it may be a good idea if you tried it and see how it feels. A little algebra of path components isn't difficult to define and implement.


Andrei
October 05, 2009
Nick Sabalausky wrote:
> "Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:hac2ku$1nfu$1@digitalmars.com...
>> Justin Johansson wrote:
>>>> "Now please name five remarkable complex literals."
>>> (re, im) ::= (0, 0), (1,0), (0,1), (1,1), (pi/2, 0), (0, pi/2), e_to_the_power_(minus j),
>>>  e_to_the_power_(minus j * pi/2)
>>>
>>> Is that what you mean?
>> (Three of those are real.)
>>
>> What I meant was that complex literals helped by syntax are seldom likely to improve code quality. Many numeric literals are of questionable taste anyway and should at best defined as symbols. I don't see why complex literals shouldn't follow the same recommendation.
>>
> 
> I think people just don't like the idea of having to deal with a distinction of "Some types can have nice handy literals but others can't."

We got to stop somewhere.

Andrei
October 05, 2009
bearophile wrote:
> (Python has such standard method, as I have described (its name is diffeernt), while C# has two methods for true and falseness of an object/struct).

Hi bearophile,

What are those two methods named in C#? I didn't know about them.
October 05, 2009
Andrei Alexandrescu Wrote:

> Justin Johansson wrote:
> > Andrei Alexandrescu Wrote:
> > 
> >> Nick Sabalausky wrote:
> >>> "Justin Johansson" <no@spam.com> wrote in message news:haavf1$2gs7$1@digitalmars.com...
> >>>> It's a difficult challenge to get high performance, readable and
> >>>> maintainable code out of complex number
> >>>> intensive algorithms.   Use of library types for complex numbers has, in
> >>>> my experience been problematic.
> >>>> Complex numbers should be first class value types I say.
> >>>>
> >>> There's been discussion before (I can't find it now, or remember the name for it) of type systems that allow for proper handling of things like m/s vs. m/(s*s) vs inch/min etc. I haven't actually worked with such a feature or with complex/imaginary numbers in any actual code, so I can't be sure, but I've been wondering if a type system like that would be an appropriate (or even ideal) way to handle real/complex/imaginary numbers.
> >> It better be. Complex numbers aren't that complicated of a notion. What's lost in pulling them out of the language is the ability to define literals.
> > 
> >> "Now please name five remarkable complex literals."
> > 
> > (re, im) ::= (0, 0), (1,0), (0,1), (1,1), (pi/2, 0), (0, pi/2), e_to_the_power_(minus j),
> >  e_to_the_power_(minus j * pi/2)
> > 
> > Is that what you mean?
> 
> (Three of those are real.)
>
> What I meant was that complex literals helped by syntax are seldom likely to improve code quality. Many numeric literals are of questionable taste anyway and should at best defined as symbols. I don't see why complex literals shouldn't follow the same recommendation.

"Three of those are real."

What?  Members of the set of real numbers are not members of the set of complex numbers?

If anything remove from the language type which is a subtype of a bigger type!!!

-- Devil's advocate :-)


P.S. Thanks for the clarification, Andrei, I understand what you mean now.


October 05, 2009
Ary Borenszweig:
> What are those two methods named in C#? I didn't know about them.

They are just named operator true and operator false. What I don't understand is why there are two of them, one of them looks enough to me:

http://msdn.microsoft.com/en-us/library/6x6y6z4d%28loband%29.aspx

http://www.blackwasp.co.uk/CSharpTrueFalseOverload.aspx

Bye,
bearophile
October 05, 2009
Andrei Alexandrescu:

> We got to stop somewhere.

The precise stopping point can be discussed.
In the new C++ they have even added some flexibility in such regard:
http://stackoverflow.com/questions/237804/user-defined-literals-in-c0x-a-much-needed-addition-or-making-c-even-more-bl

Bye,
bearophile