January 03, 2016
On 1/1/16 6:30 AM, Russel Winder via Digitalmars-d wrote:
> On Fri, 2016-01-01 at 10:40 +0000, Kapps via Digitalmars-d wrote:
>> […]
>>
>> Someone else can explain better / more correctly than me, but I
>> believe the issue lies with opCmp and opEquals. You can make
>> expressions like p.Name.equals("James") work (I believe using
>> opDispatch), but because all you have is opEquals, you can't know
>> if the user put in 'p.Name == "James"` or `p.Name != "James"`, as
>> they both simply call opEquals. In order to do that, you would
>> need things like opLessThan, opEquals, opNotEquals,
>> opGreaterThan, etc, which would (with improper use or bugs) cause
>> other issues, like a < b && a > b and a == b && a != b to be
>> true, or a == b || a != b to be false.
>>
>> I'm also not certain how you could implement `p => p.Name ==
>> "James" || p.Name == "Bob"`, but there might be a way? I think
>> this is the gist of it, but I'm likely wrong on some aspects of
>> this, so it would be good if someone else clarified..
>
> Hummm… so to put it another way, the D meta-object protocol is even
> more broken than that of Java: at least in Java there isn't even a
> pretence that you can create an internal DSL. This is very, very sad, I
> had not realized D was this broken.

Well I guess it's time to cut losses and cancel that D tutorial at ACCU 2016, eh :o).

I understand how the shortcomings of D's expression templates (ETs) for SQL (as compared to C++ or LINQ) can be construed as an indication of a language breakage, but I don't quite agree for a few reasons.

One, D offers another mechanism aside from ETs for embedded domain-specific languages. So in a way there's less pressure on offering lush ET than would be in other languages.

Second, ET as a mechanism for SQL interface has other inherent limitations. Consider the "LIKE" operator in SQL, which has no ET equivalent in C++ with similar syntax, and no direct equivalent in LINQ. That doesn't mean the respective languages are broken.

Third, evaluating the merits and demerits of a language choice should be done within several appropriate use cases, not just one. For example, D's use of opEquals/opCmp saves a lot of boilerplate and potential bugs in many cases compared to C++. On another example, C++'s overloading of &&, ||, and the comma operator are considered downright disastrous and are unrecommended by virtually all coding standards and guidelines, yet do work with ETs.


Andrei

January 03, 2016
On 1/2/16 7:23 AM, Jacob Carlborg wrote:
> On 2016-01-01 11:45, Ola Fosheim Grøstad wrote:
>
>> In D1 Walter made a point about restricting operator overloading to
>> discourage reuse of operators. In D2 there are many ways to create your
>> own weird syntax, but Walter is locked on his D1 position, even though
>> it makes little sense in D2.
>>
>> This is an overarching theme in D: the design rationale, that goes a
>> decade back and is based on making a restricted easy to use version of
>> C++, does not change, even though the context is very different in 2015
>> and the premises has changed.
>>
>> These things are unlikely to change without a fork. Which is a pitty as
>> D needs a more coherent design to get out of stagnation.
>
> The core developers are making a big deal out of being able to have
> DSL's as string literals and process them at compile time. Although
> that's kind of pointless with SQL, since one still needs to send to
> string to the database to perform the query. The only thing that's
> possible is to validate the syntax at compile time.

Binding D variables to SQL expressions also comes to mind. -- Andrei


January 04, 2016
On Monday, 4 January 2016 at 00:49:29 UTC, Andrei Alexandrescu wrote:
>
> Second, ET as a mechanism for SQL interface has other inherent limitations. Consider the "LIKE" operator in SQL, which has no ET equivalent in C++ with similar syntax, and no direct equivalent in LINQ. That doesn't mean the respective languages are broken.
>

LINQ transforms StartsWith, EndsWith and Contains methods in LIKE queries under the hood since Entity Framework 4.0 (8 years ago).
Also, when in doubt, there is an entire namespace dedicated to LINQ queries with direct SQL equivalents (including LIKE) - https://msdn.microsoft.com/en-us/library/system.data.linq.sqlclient.sqlmethods(v=vs.110).aspx



January 04, 2016
On 2016-01-04 01:49, Andrei Alexandrescu wrote:

> Second, ET as a mechanism for SQL interface has other inherent
> limitations. Consider the "LIKE" operator in SQL, which has no ET
> equivalent in C++ with similar syntax, and no direct equivalent in LINQ.
> That doesn't mean the respective languages are broken.

It's one operator that doesn't have a direct corresponding in the host language. With D, it's not possible to handle most of the operators.

> Third, evaluating the merits and demerits of a language choice should be
> done within several appropriate use cases, not just one. For example,
> D's use of opEquals/opCmp saves a lot of boilerplate and potential bugs
> in many cases compared to C++.

If D allowed to separately overload the comparison operators the standard library should of course provide a mixin of some kind to reduce the boilerplate.

> On another example, C++'s overloading of
> &&, ||, and the comma operator are considered downright disastrous and
> are unrecommended by virtually all coding standards and guidelines, yet
> do work with ETs.

I think for generating SQL, overloading && and || would be a valid use case.

There's of course AST macros as well, which have many other good use cases. Unfortunately you don't like those either :(

-- 
/Jacob Carlborg
January 04, 2016
On 2016-01-03 17:45, Sebastiaan Koppe wrote:

> Suppose you have this:
>
> mixin(db(`
> Entity Person
>    Fields
>      name -> string
>      age -> integer
>    Query
>      byAge(a -> integer) -> age == a
> `));
>
> which generates something like this:
>
> struct Person
> {
>    string name;
>    int age
> }
> auto getPersonByAge(DB db, int a)
> {
>    return db.prepare!Person("SELECT name,age FROM Person WHERE age =
> ?").query(a);
> }
>
> and then later in time:
>
> mixin(db(`
> Entity Person
>    Fields
>      name -> string
>      age -> integer
>      phone -> string
>    Query
>      byAge(a -> integer) -> age == a
> `));
>
> Given that you have access to both version it is easy to generate
> migration code for the phone field.
>
> Maybe it is contrived, but I think it shows you can do more with the DSL
> than just validating queries.

Perhaps I'm missing something obvious but there are several problems with this:

1. What happens when you use more than one query for the same table at the same scope? In the above case, "Person" is already defined the second time "db" is invoked. It's not possible to add fields for already declared structs. Unless you use some form of opDispatch backed by an associative array of variants

2. What happens if I want to execute a custom query in a function, i.e. a query that is only used once. Will it generate the sturct inside the function or am I forced to always use this mixin at module level?

I still think it's a lot easier to declare the struct with standard D code. I don't think the DSL adds any value in this case. Just do something like:

@db struct Person
{
    string name;
    int age;
}

The @db attribute would allow to create the migrations.

-- 
/Jacob Carlborg
January 04, 2016
On 2016-01-04 01:50, Andrei Alexandrescu wrote:

> Binding D variables to SQL expressions also comes to mind. -- Andrei

You have also been pushing a lot for ranges, which I think is good. I would much rather like to view a table as a range, but the algorithms would be preform in the database instead of in the application code. Something like:

Person.filter!(e => e.name == "John")

-- 
/Jacob Carlborg
January 04, 2016
On 2016-01-02 21:47, Chris Wright wrote:

> So you want to create the following query:
>
>    people.filter!(x => x.surname == "Slughorn");
>
> And you've got ten million people in the collection, and you want this
> query to finish soonish. So you need to use an index. But a full index
> scan isn't so great; you want to do an index lookup if possible.
>
> That's simple enough; we generate proxy types to record what properties
> you're using and what operations you're performing. PersonProxy records
> that you're accessing a field 'surname', gives a StringFieldProxy, and
> that records that you're checking for equality with the string "Slughorn".
> The lambda returns true when opEquals returns true.
>
> But people write queries that are more complex than that, like:
>
>    people.filter!(x => x.surname == "Slughorn" || x.age <= 17);
>
> First time you run this, x.surname.opEquals("Slughorn") returns true and
> the expression as a whole returns true. You missed the second part of the
> expression. That's bad.

If D had better operator overloading or supported AST macros, opEquals would not return true. It would return a new proxy that overloads the || operator. The rest of the post falls apart from this mistake.


-- 
/Jacob Carlborg
January 04, 2016
On 2016-01-04 00:50, Andrei Alexandrescu wrote:

> This may in fact be good signal that an approach based on expression
> templates is not the most appropriate for D. -- Andrei

This whole thread has already discussed and showed that D operator overloading is lacking in terms of expression templates. The post by Chris assumes no language changes. If D supported overloading all operators the opEquals method would not return true, it would return a proxy that overloaded the || operator. The whole lambda expression would only generate a single query. The rest of the post falls apart from this mistake.

-- 
/Jacob Carlborg
January 04, 2016
On Monday, 4 January 2016 at 07:55:53 UTC, Jacob Carlborg wrote:
> On 2016-01-02 21:47, Chris Wright wrote:
>
>> So you want to create the following query:
>>
>>    people.filter!(x => x.surname == "Slughorn");
>>
>> And you've got ten million people in the collection, and you want this
>> query to finish soonish. So you need to use an index. But a full index
>> scan isn't so great; you want to do an index lookup if possible.
>>
>> That's simple enough; we generate proxy types to record what properties
>> you're using and what operations you're performing. PersonProxy records
>> that you're accessing a field 'surname', gives a StringFieldProxy, and
>> that records that you're checking for equality with the string "Slughorn".
>> The lambda returns true when opEquals returns true.
>>
>> But people write queries that are more complex than that, like:
>>
>>    people.filter!(x => x.surname == "Slughorn" || x.age <= 17);
>>
>> First time you run this, x.surname.opEquals("Slughorn") returns true and
>> the expression as a whole returns true. You missed the second part of the
>> expression. That's bad.
>
> If D had better operator overloading or supported AST macros, opEquals would not return true. It would return a new proxy that overloads the || operator. The rest of the post falls apart from this mistake.

compiler plugins like Rust to enable AST macros could fix this but walter and andrei seem extremely opposed to any form of macros so we end up with ugly heavily templated ugly band-aids

shame, because I think D will quickly lose their lead in the metaprogramming area if Rust keeps it up - e.g, someone already made a GC Plugin for rust's compiler
January 04, 2016
On 1/1/2016 3:33 AM, Russel Winder via Digitalmars-d wrote:
> Or alternative 4, fix D so that proper operator definition can be
> achieved.

The way D's operator overloading is designed is not a mistake. It's limitations are a feature, not a bug. It was deliberately set up to:

1. Make doing C++ style iostreams hard.

2. Prevent clever use of operator overloading and expression templates to create languages that look like D, but are NOT.

3. Work well when using operator overloading to implement arithmetic types.

For example, I've seen operator overloading used in C++ to turn it into a sort-of regex language. The failures of it are:

1. Sort-of because C++ operator precedence and prefix/postfix grammar is different than that of regex, so it can't be emulated correctly.

2. It is visually indistinguishable from C++ code. You simply cannot look at a piece of code and tell it is regex with utterly different meaning, rather than the usual meanings.

3. Any error messages from misuse are utterly and totally incomprehensible, because the compiler is designed to compile C++ and gives C++ messages, not regex messages.

4. C++ ETs are legendary in their tendency to consume all the memory in the computer and take incredibly long to compile. Most fail when the expressions exceed a rather small level of complexity because of this.

My not-so-humble opinion is these sorts of DSLs are technical demonstrations, but not useful nor desirable tools.

***************************************
So, what does D do?

   http://dlang.org/phobos/std_regex.html

D enables CTFE to write a PROPER compile time regex language, with correct regex grammar, correct regex tokens, regex-specific error messages, etc. And it works great! It's not a hack, compromise, or workaround. It's a real, embedded DSL. And it's faster than any other regex engine on the market.