March 08, 2010
Ary Borenszweig wrote:
> Walter Bright wrote:
>> Lots of meat and potatoes here, and a cookie! (spelling checker for error messages)
>>
>> http://www.digitalmars.com/d/1.0/changelog.html
>> http://ftp.digitalmars.com/dmd.1.057.zip
>>
>>
>> http://www.digitalmars.com/d/2.0/changelog.html
>> http://ftp.digitalmars.com/dmd.2.041.zip
>>
>> Thanks to the many people who contributed to this update!
> 
> bearophile must be very happy :-)
> 
> - Add !in operator.
> - opCast(bool) and implicit call in if
> 
> But I don't like this: "This only happens, however, for instances of structs. Class references are converted to bool by checking to see if the class reference is null or not."
> 
> When I do:
> 
> if (x) {
> }
> 
> I'm normally interested to enter the if branch if x is not null and it has an interesting value. For example, if I implement a String class I would implement it as not being empty. But I think the biggest problem is making a different semantic for this to structs and classes. You'll have programmers wondering why the if branch was entered even if my class implemented opBool not to enter it. But time will tell.
> 
> For opCast maybe it would be better to have some examples, like:
> 
> T opCast(T)() if (is(T == bool)) {
> }
> 
> T opCast(T)() if (is(T == int)) {
> }
> 
> (it's not very intuitive that I have to do that to implement opCast, it's different from the other operator overloading that rely on strings)
> 
> But very cool that opCast to many different types is implemented! :-)

And isn't it better for the spellchecker to show the closest match using levenshtein distance instead of just distance 1?
March 08, 2010
The Html of the change logs seems a bit wrong, the latest version now is at the top, and in the 2.0 changelog the "Bugs Fixed" are divided in two different horizontal alignments.
March 08, 2010
On Mon, 08 Mar 2010 01:54:12 -0500, Walter Bright <newshound1@digitalmars.com> wrote:

> Lots of meat and potatoes here, and a cookie! (spelling checker for error messages)
>
> http://www.digitalmars.com/d/1.0/changelog.html
> http://ftp.digitalmars.com/dmd.1.057.zip
>
>
> http://www.digitalmars.com/d/2.0/changelog.html
> http://ftp.digitalmars.com/dmd.2.041.zip
>
> Thanks to the many people who contributed to this update!

This might belong on d.learn, but it's so specific to this release, I'll ask here:

The implementation of opEquals(Object, Object) contains the following implementation (according to docs and source):

bool opEquals(Object a, Object b) {
    if (a is b) return true;
    if (a is null || b is null) return false;
    if (typeid(a) == typeid(b)) return a.opEquals(b);
    return a.opEquals(b) && b.opEquals(a);
}

The third line seems to be a recursive call into opEquals, since the typeinfos are objects.  But because the two typeid classes have the same typeinfo (TypeInfo_Class with class name "TypeInfo_Class") it will only go 2 levels deep since the first if clause will be true.  However, it seems a bit redundant to verify that the typeids are not null (they will always be non-null) and have the same typeinfo (they will).

Wouldn't it be more efficient and correct to just do:

if(typeid(a).opEquals(typeid(b)))

Also, there is a function inside object implementation to compare two typeinfos (commented out in interface file), is that used?  Does the compiler allow overloading the global opEquals?

Also, const is not respected:

class C
{
   int x;
   bool opEquals(Object other){ ++x; return false;}
}
void main()
{
   const c = new C;
   const d = new D;
   assert(c != d);
}

compiles without complaint.  I would argue that opEquals should be const in both Object and the global function, but I'm not sure of the ramifications.  At the very least, the above should not compile.

-Steve
March 08, 2010
Ary Borenszweig:
> And isn't it better for the spellchecker to show the closest match using levenshtein distance instead of just distance 1?

Walter is an engineer, and follows the KISS principle, he likes to use the simpler thing that can possibly work (especially in the first version of something). I think the current spellchecker can be good enough.

Bye,
bearophile
March 08, 2010
Ary Borenszweig:
> I'm normally interested to enter the if branch if x is not null and it has an interesting value. For example, if I implement a String class I would implement it as not being empty. But I think the biggest problem is making a different semantic for this to structs and classes. You'll have programmers wondering why the if branch was entered even if my class implemented opBool not to enter it. But time will tell.

A solution can be to disallow opCast!(bool) in classes. So when in your code you convert a struct (that already defines opCast!(bool)) to a class, or when you try to add opCast!(bool) to a class, the compiler gives you a nice compilation error, and saves you from possible bugs.
A warning too can be better than nothing.


> For opCast maybe it would be better to have some examples, like:
> 
> T opCast(T)() if (is(T == bool)) {
> }
> 
> T opCast(T)() if (is(T == int)) {
> }
> 
> (it's not very intuitive that I have to do that to implement opCast, it's different from the other operator overloading that rely on strings)

This works:

import std.stdio: writeln;

struct Foo {
    int len;

    this(int x) {
        this.len = x;
    }

    T opCast(T:bool)() {
        return this.len != 0;
    }
    T opCast(T:int)() {
        return this.len;
    }
}

void main() {
    auto f = Foo(5);

    if (f)
        writeln("true");
    else
        writeln("false");

    writeln(cast(int)f);
}

Bye,
bearophile
March 08, 2010
bearophile wrote:
> Ary Borenszweig:
>> I'm normally interested to enter the if branch if x is not null and it has an interesting value. For example, if I implement a String class I would implement it as not being empty. But I think the biggest problem is making a different semantic for this to structs and classes. You'll have programmers wondering why the if branch was entered even if my class implemented opBool not to enter it. But time will tell.
> 
> A solution can be to disallow opCast!(bool) in classes. So when in your code you convert a struct (that already defines opCast!(bool)) to a class, or when you try to add opCast!(bool) to a class, the compiler gives you a nice compilation error, and saves you from possible bugs.
> A warning too can be better than nothing.
> 
> 
>> For opCast maybe it would be better to have some examples, like:
>>
>> T opCast(T)() if (is(T == bool)) {
>> }
>>
>> T opCast(T)() if (is(T == int)) {
>> }
>>
>> (it's not very intuitive that I have to do that to implement opCast, it's different from the other operator overloading that rely on strings)
> 
> This works:
> 
> import std.stdio: writeln;
> 
> struct Foo {
>     int len;
> 
>     this(int x) {
>         this.len = x;
>     }
> 
>     T opCast(T:bool)() {
>         return this.len != 0;
>     }
>     T opCast(T:int)() {
>         return this.len;
>     }
> }

Cool! Much, much nicer. (that's why I think some examples would be fine there)
March 08, 2010
Is there a better way to use the new operator overloading than string mixins?

Also the following code strangely yields:
dsfml\system\vector2.d(47): Error: variable dsfml.system.vector2.Vector2!(float).Vector2.op only parameters or foreach declarations can be ref

/// element-wise operations, +, -,
ref Vector2 opBinary(string op)(ref Vector2 v)
{
	mixin("return Vector2!(T)( cast(T)(x " ~ op ~ " v.x), cast(T)(y " ~ op ~ " v.y) );");
}

Removing ref from the return type makes it compile.


Furthermore the assignment operator seems to be rewritten as opBinary instead of opAssign as the docs state:

Vector2f	_pos = Vector2f(0.f, 0.f);
yields:
Error: template instance opBinary!("=") matches more than one template declaration


This also shows another problem. It can't distinguish between these two:
Vector2 opBinary(string op)(ref Vector2 v)
if (op != "*")
{
	mixin("return Vector2!(T)( cast(T)(x " ~ op ~ " v.x), cast(T)(y " ~ op ~ " v.y) );");
}

Vector2 opBinary(string op)(int i)
{
	mixin("return Vector2!(T) ( cast(T)(x " ~ op ~ " i), cast(T)(y " ~ op ~ " i) );");
}

even though vec1 + vec2 resp. vec + 5 is unambiguous.
March 08, 2010
> __traits allMembers and and derivedMembers now return a tuple of strings rather
> than an array of strings. Enclose __traits in [ ] to make array literal. This makes it
> possible for foreach statements to iterate at compile time over it.

How exciting!

On 3/8/10, Trass3r <un@known.com> wrote:
> Is there a better way to use the new operator overloading than string mixins?
>
> Also the following code strangely yields:
> dsfml\system\vector2.d(47): Error: variable
> dsfml.system.vector2.Vector2!(float).Vector2.op only parameters or foreach
> declarations can be ref
>
> /// element-wise operations, +, -,
> ref Vector2 opBinary(string op)(ref Vector2 v)
> {
> 	mixin("return Vector2!(T)( cast(T)(x " ~ op ~ " v.x), cast(T)(y " ~ op ~ "
> v.y) );");
> }
>
> Removing ref from the return type makes it compile.
>
>
> Furthermore the assignment operator seems to be rewritten as opBinary instead of opAssign as the docs state:
>
> Vector2f	_pos = Vector2f(0.f, 0.f);
> yields:
> Error: template instance opBinary!("=") matches more than one template
> declaration
>
>
> This also shows another problem. It can't distinguish between these two:
> Vector2 opBinary(string op)(ref Vector2 v)
> if (op != "*")
> {
> 	mixin("return Vector2!(T)( cast(T)(x " ~ op ~ " v.x), cast(T)(y " ~ op ~ "
> v.y) );");
> }
>
> Vector2 opBinary(string op)(int i)
> {
> 	mixin("return Vector2!(T) ( cast(T)(x " ~ op ~ " i), cast(T)(y " ~ op ~ "
> i) );");
> }
>
> even though vec1 + vec2 resp. vec + 5 is unambiguous.
>
March 08, 2010
Walter Bright wrote:
> Lots of meat and potatoes here, and a cookie! (spelling checker for error messages)
> 
> http://www.digitalmars.com/d/1.0/changelog.html
> http://ftp.digitalmars.com/dmd.1.057.zip
> 
> 
> http://www.digitalmars.com/d/2.0/changelog.html
> http://ftp.digitalmars.com/dmd.2.041.zip
> 
> Thanks to the many people who contributed to this update!

A few changes that I made to Phobos and also put in the changelog don't appear in this release because I forgot to commit changelog.dd itself.

    $(WHATSNEW
	$(LI string, wstring are now bidirectional (not random) ranges)
	$(LI std.algorithm: defined move with one argument; levenshtein distance generalized to with all forward ranges; take now has swapped arguments)
	$(LI std.array: empty for arrays is now a @property; front and back for a string and wstring automatically decodes the first/last character; popFront, popBack for string and wstring obey the UTF stride)
	$(LI std.conv: changed the default array formatting from "[a, b, c]" to "a b c")
	$(LI std.range: swapped order of arguments in take)
	$(LI std.stdio: added readln template)
	$(LI std.variant: now works with statically-sized arrays and const data)
	$(LI std.traits: added isNarrowString)
    )


Andrei
March 08, 2010
Andrei Alexandrescu:
> 	$(LI std.conv: changed the default array formatting from "[a, b, c]" to
> "a b c")

That's a regression!!!
(And I think in the past it was [a,b,c] instead of [a, b, c], because it's better to save some screen space, it costs a lot!).

Bye,
bearophile