September 14, 2015
On 09/13/2015 10:06 AM, Martin Nowak wrote:
> On 09/13/2015 05:03 AM, Andrei Alexandrescu wrote:
>> Yah, understood. Problem here is the approach is bound to run into walls
>> at every few steps. Say you fix the comparisons to work. Then you have
>> operators such as LIKE that are necessary yet not representable in D. So
>> more tricks are in order. This is what I've seen with ET in C++ - an
>> endless collection of tricks to achieve modest outcomes at enormous costs.
>
> But this is not an argument to rule out `opBinary!"<"`.

Agreed.

> To summarize the arguments.
>
> - logical indexing x[x < 20]
>
>    e.g. opBinary!"<" returns a bit mask to select entries of a large vector

Good one. But niche, can be solved with a function, etc.

> - faster comparison
>
>    struct Foo
>    {
>        size_t id;
>        int opCmp(Foo rhs)
>        {
>            if (id < rhs.id) return -1;
>            if (id == rhs.id) return 0;
>            else return 1;
>        }
>        bool opBinary(string s:"<")(Foo rhs)
>        {
>            return id < rhs.id;
>        }
>    }
>
>    Sorting a million Foos w/ random ids is 37.5% slower with opCmp.
>
>    foos.sort!((a, b) => a.opBinary!"<"(b))(); // 104ms
>    foos.sort!((a, b) => a < b)(); // 143ms

I think this is a good candidate for a peephole optimization.

> - expression templates
>
>    I'm well aware of the limitations, but still think it will work out
> nicely for an ORM b/c there is precedence in other language, e.g. Rails'
> (ActiveRecord) query syntax.

Fine, but let's not forget the ET experience in D will be a lot closer to that in C++ than that in Rails. And that doesn't bode well.

> - language regularization
>
>    It's surprising to find these "arbitrary" language limitations.
>    The non-predictability of what's possible has always been a huge issue
> for me with C++, i.e. you come up with an idea, spend 4 hours
> implementing it only to find out that the small detail x isn't feasible.

This is the case in all powerful languages. Martin Odersky told me there's a constant problem with Scala having such a powerful type systems, the envelope of what can and cannot be done with it is really fuzzy and frustrates its power users.


Andrei

September 14, 2015
On Monday, 14 September 2015 at 13:47:10 UTC, Sebastiaan Koppe wrote:
> `auto q = query.builder!Person.age!">"(20).name("Peter");`

I confess that I'm not really paying attention to this thread, but I can't help but think plain old literal: `Person.age > 20 && Person.name = 'Peter'` is nicer. You can still CT check that by parsing the string if you want.
September 14, 2015
On 09/13/2015 01:16 PM, Daniel N wrote:
> On Sunday, 13 September 2015 at 14:06:46 UTC, Martin Nowak wrote:
>>   struct Foo
>>   {
>>       size_t id;
>>       int opCmp(Foo rhs)
>>       {
>>           if (id < rhs.id) return -1;
>>           if (id == rhs.id) return 0;
>>           else return 1;
>>       }
>>       bool opBinary(string s:"<")(Foo rhs)
>>       {
>>           return id < rhs.id;
>>       }
>>   }
>>
>>   Sorting a million Foos w/ random ids is 37.5% slower with opCmp.
>>
>
> Could you try this?
>
> int opCmp(Foo rhs)
> {
>    return (id > rhs.id) - (id < rhs.id);
> }

Apparently that does well with gcc: http://stackoverflow.com/questions/10996418/efficient-integer-compare-function -- Andrei

September 14, 2015
On 09/13/2015 03:06 PM, Martin Nowak wrote:
> On 09/13/2015 07:16 PM, Daniel N wrote:
>>
>> Could you try this?
>>
>> int opCmp(Foo rhs)
>> {
>>    return (id > rhs.id) - (id < rhs.id);
>> }
>
> That's not the point, opCmp requires twice as many comparisons as needed
> for <. If they are more expansive, e.g. string comparison, your trick
> won't work.

Well for string comparisons, the added cost (if not optimized away) is constant. Overall it seems to me that D is to be appreciated for requiring only one operator for all ordering comparisons (it causes a lot of noise in C++). Reducing comparisons is the job of the optimizer. Expression templates are a possible loss, but of a questionable trick. -- Andrei

September 14, 2015
On 09/14/2015 07:32 AM, burjui wrote:
> On Sunday, 13 September 2015 at 17:16:40 UTC, Daniel N wrote:
>> int opCmp(Foo rhs)
>> {
>>   return (id > rhs.id) - (id < rhs.id);
>> }
>
> IMO, subtracting boolean values is bad code style

I love it! -- Andrei

September 14, 2015
On Monday, 14 September 2015 at 18:24:00 UTC, Andrei Alexandrescu wrote:
> On 09/14/2015 07:32 AM, burjui wrote:
>> On Sunday, 13 September 2015 at 17:16:40 UTC, Daniel N wrote:
>>> int opCmp(Foo rhs)
>>> {
>>>   return (id > rhs.id) - (id < rhs.id);
>>> }
>>
>> IMO, subtracting boolean values is bad code style
>
> I love it! -- Andrei

Beside loving, it, this has good ILP, no branches, and have nice codegen on most CPUs.

I was surprised to find that style in jemalloc, and compared to various alternatives. The only way I was able to get better is by ignoring (and by extension getting invalid results) overflow.

September 14, 2015
On 09/14/2015 08:18 PM, Andrei Alexandrescu wrote:
> On 09/13/2015 01:16 PM, Daniel N wrote:
>> On Sunday, 13 September 2015 at 14:06:46 UTC, Martin Nowak wrote:
>>>   struct Foo
>>>   {
>>>       size_t id;
>>>       int opCmp(Foo rhs)
>>>       {
>>>           if (id < rhs.id) return -1;
>>>           if (id == rhs.id) return 0;
>>>           else return 1;
>>>       }
>>>       bool opBinary(string s:"<")(Foo rhs)
>>>       {
>>>           return id < rhs.id;
>>>       }
>>>   }
>>>
>>>   Sorting a million Foos w/ random ids is 37.5% slower with opCmp.
>>>
>>
>> Could you try this?
>>
>> int opCmp(Foo rhs)
>> {
>>    return (id > rhs.id) - (id < rhs.id);
>> }
>
> Apparently that does well with gcc:
> http://stackoverflow.com/questions/10996418/efficient-integer-compare-function
> -- Andrei
>

The first version was actually slightly faster when I tested it with std::sort. (i.e. if only "<" is used). I guess it is optimized to a simple integer comparison after inlining. In any case, any performance differences for such simple equivalent functions are random quirks of the respective back-ends.
September 14, 2015
On 09/14/2015 08:09 PM, Andrei Alexandrescu wrote:
> On 09/13/2015 10:06 AM, Martin Nowak wrote:
>> ...
>> - language regularization
>>
>>    It's surprising to find these "arbitrary" language limitations.
>>    The non-predictability of what's possible has always been a huge issue
>> for me with C++, i.e. you come up with an idea, spend 4 hours
>> implementing it only to find out that the small detail x isn't feasible.
>
> This is the case in all powerful languages.

That's an overgeneralization.

Furthermore, having arbitrary designed-in irregularities is not comparable to implementing a system whose emergent behavior is not understood sufficiently well. (D is guilty of both, but the former is much easier to fix than the latter.)

> Martin Odersky told me there's a constant problem with Scala ...

Both C++ and Scala have accidentally Turing-complete type systems.
September 14, 2015
On Monday, 14 September 2015 at 19:35:39 UTC, Timon Gehr wrote:
> Furthermore, having arbitrary designed-in irregularities is not comparable to implementing a system whose emergent behavior is not understood sufficiently well. (D is guilty of both, but the former is much easier to fix than the latter.)

Well, D1 was a restrictive design that made C++ style programming simpler and less error prone (sans compiler bugs). D2 is a flexible and complicated package and thereby those restrictions cause disharmony. It is basically too incompatible design philosophies that are brought together.

Like in D1 it would make sense to say "the language provide the essentials, don't design your own pointer types", whereas in D2 it makes sense to say "all features should be library based". I'm a bit miffed that it is difficult to built proper smart pointer types in D2.

> Both C++ and Scala have accidentally Turing-complete type systems.

Yes, not sure how smart that is in terms of maintainable code. There is a reaction against overdone type systems that sometimes can be more of burden as programs age/grow (e.g. C++/Boost), so now we see gradual typing, deliberately unsound type-systems etc.

September 15, 2015
On Monday, 14 September 2015 at 18:17:05 UTC, Adam D. Ruppe wrote:
> On Monday, 14 September 2015 at 13:47:10 UTC, Sebastiaan Koppe wrote:
>> `auto q = query.builder!Person.age!">"(20).name("Peter");`
>
> I confess that I'm not really paying attention to this thread, but I can't help but think plain old literal: `Person.age > 20 && Person.name = 'Peter'` is nicer. You can still CT check that by parsing the string if you want.

It is definitely nicer, but this is also a contrived use-case. What happens when you have a couple of joins, how would you write that? Or inner queries?

Suppose we wanted to fetch all users who didn't place an order in the last year, it would be as simple as calling `.lastOrderDate!"<"(lastYear)`. It would do the join with the order table on the appropriate column.

Granted, someone has to write `lastOrderDate()`.