Thread overview
opEquals() non-standard return type
Jan 23, 2019
Jacob Shtokolov
Jan 23, 2019
Jonathan M Davis
Jan 23, 2019
Jacob Shtokolov
Jan 23, 2019
H. S. Teoh
Jan 24, 2019
Jacob Shtokolov
Jan 24, 2019
Ali Çehreli
Jan 24, 2019
Jacob Shtokolov
Jan 24, 2019
Neia Neutuladh
January 23, 2019
Hi,

I'm trying to check whether it's possible to implement Python's SQLAlchemy-like query syntax in D, but I get stuck a bit.

Here is a simple example of what I want to achieve:

```
auto result = User.filter(User.id == 10);
result = User.filter(User.name == "John");
result = User.filter(User.age > 18);
```

Expressions like `User.id == 10`, `User.age > 18`, etc. should return a struct instead of a bool (let's call it `struct BinaryExpression`).

So I'm making the two versions of opEquals: one returns a BinaryExpression, and the second - a boolean value.

However, when I want to use the same expression for the `if` operator, the compiler cannot decide what function to call and shows an error: "overloads bool(int b) and BinaryExpr!int(int b) both match argument list for opEquals".


I'm wondering, is that possible to declare multiple versions of opEquals() and evaluate them in the different places depending on return type?

Here is my test code to check: https://run.dlang.io/is/yTFHWp
Gist: https://gist.github.com/run-dlang/67ec42ca73d56d310e8ae765fabede69

Thanks!
January 23, 2019
On Wednesday, January 23, 2019 8:19:06 AM MST Jacob Shtokolov via Digitalmars-d-learn wrote:
> Hi,
>
> I'm trying to check whether it's possible to implement Python's SQLAlchemy-like query syntax in D, but I get stuck a bit.
>
> Here is a simple example of what I want to achieve:
>
> ```
> auto result = User.filter(User.id == 10);
> result = User.filter(User.name == "John");
> result = User.filter(User.age > 18);
> ```
>
> Expressions like `User.id == 10`, `User.age > 18`, etc. should return a struct instead of a bool (let's call it `struct BinaryExpression`).
>
> So I'm making the two versions of opEquals: one returns a BinaryExpression, and the second - a boolean value.
>
> However, when I want to use the same expression for the `if` operator, the compiler cannot decide what function to call and shows an error: "overloads bool(int b) and BinaryExpr!int(int b) both match argument list for opEquals".
>
>
> I'm wondering, is that possible to declare multiple versions of opEquals() and evaluate them in the different places depending on return type?
>
> Here is my test code to check: https://run.dlang.io/is/yTFHWp Gist: https://gist.github.com/run-dlang/67ec42ca73d56d310e8ae765fabede69
>
> Thanks!

D's operator overloading is specifically designed around the idea that overloaded operators are supposed to act like the operators on the built-in types and that they _not_ be used for building syntax. opEquals is supposed to only return bool. If you attempt to make it return pretty much anything else, you're begging for trouble.

But regardless of the specifics of operator overloading in D, D does not support overloading _any_ functions on the return type. Overloading is only done based an a function's arguments. You've declared two overloads with the exact same types for all of their parameters such that they only differ by their return type, and you can't do that in D.

- Jonathan M Davis



January 23, 2019
On Wednesday, 23 January 2019 at 15:28:02 UTC, Jonathan M Davis wrote:
> But regardless of the specifics of operator overloading in D, D does not support overloading _any_ functions on the return type.

Thanks!
January 23, 2019
On Wed, Jan 23, 2019 at 03:19:06PM +0000, Jacob Shtokolov via Digitalmars-d-learn wrote:
> Hi,
> 
> I'm trying to check whether it's possible to implement Python's SQLAlchemy-like query syntax in D, but I get stuck a bit.
> 
> Here is a simple example of what I want to achieve:
> 
> ```
> auto result = User.filter(User.id == 10);
> result = User.filter(User.name == "John");
> result = User.filter(User.age > 18);
> ```
[...]

The best way to do this is to use a string DSL or a delegate as template argument. For example:

	auto result = User.filter!q{ User.name == "John" };

or:

	auto result = User.filter!(u => u.name == "John");

The delegate option will be easier to implement, but the syntax will be slightly more verbose.  The string DSL option will give you the best syntax, but then you'll have to implement a compile-time DSL parser.


T

-- 
Bare foot: (n.) A device for locating thumb tacks on the floor.
January 23, 2019
On 01/23/2019 07:19 AM, Jacob Shtokolov wrote:

> Expressions like `User.id == 10`, `User.age > 18`, etc. should return a
> struct instead of a bool (let's call it `struct BinaryExpression`).

Have you considered 'alias this'?

struct BinaryExpr(T)
{
  T left;
  T right;
  Op op;

  bool value() const {
    final switch (op) with (Op) {
      case Equals:
        return left == right;
      case NotEquals:
        return left != right;
    }
  }

  alias value this;
}

> So I'm making the two versions of opEquals: one returns a
> BinaryExpression, and the second - a boolean value.

Yeah, that can't work. Remove the bool-returning one and your code works with the 'alias this' above.

Ali

January 24, 2019
On Wed, 23 Jan 2019 15:19:06 +0000, Jacob Shtokolov wrote:
> I'm wondering, is that possible to declare multiple versions of opEquals() and evaluate them in the different places depending on return type?

I looked at this a while ago for similar reasons. It didn't pan out.

When you override the comparison operator, you can't distinguish which comparison is overloaded. It takes at least 2^n evaluations of the function to determine all the comparisons, possibly more. (I *think* you can observe the short-circuiting logic as you do those evaluations to determine how each comparison is combined.)
January 24, 2019
On Thursday, 24 January 2019 at 00:47:37 UTC, Ali Çehreli wrote:
> Yeah, that can't work. Remove the bool-returning one and your code works with the 'alias this' above.

Wow, this is an amazing workaround! I didn't think about it in that way.
It perfectly solves the issue.

Thank you!
January 24, 2019
On Wednesday, 23 January 2019 at 17:28:37 UTC, H. S. Teoh wrote:
> The best way to do this is to use a string DSL or a delegate as template argument. For example:
>
> 	auto result = User.filter!q{ User.name == "John" };
>
> or:
>
> 	auto result = User.filter!(u => u.name == "John");
>

I didn't know about q{} token strings! This looks very cool for implementing different DSLs.

Thank you!