Re: Generic operator overloading for immutable types?

2017-06-14 Thread Kagamin via Digitalmars-d-learn

On Tuesday, 13 June 2017 at 19:29:26 UTC, Gary Willoughby wrote:
Is it possible for the `result` variable in the following code 
to be returned as an immutable type if it's created by adding 
two immutable types?


Why do you even want that? Such plain data structure is 
implicitly convertible to any const flavor: 
https://dpaste.dzfl.pl/c59c4c7131b2


Re: Generic operator overloading for immutable types?

2017-06-14 Thread ag0aep6g via Digitalmars-d-learn

On 06/14/2017 03:47 AM, Steven Schveighoffer wrote:
The fundamental difference is that const and immutable share a 
characteristic that mutable doesn't -- you can't mutate the data.


(... through the reference at hand.)

const and mutable share this: The data may be mutated from elsewhere.

Mutable shares as much with const as immutable does. But it's the other 
side of const, of course.


The reason const(inout) works is because const(immutable) evaluates to 
just immutable.


const() just evaluates to const. In this way, mutable is less 
"special" than immutable.


Yeah. That makes it impossible to express the proposed type. But it's 
just because of the immutable > const > mutable progression in D, which 
makes for arbitrary asymmetries between mutable and immutable.


If it were const > mutable = immutable (and if we had a `mutable` 
keyword), then `inout immutable` would work like today's `inout const`, 
`inout mutable` would be what I'm talking about, and `inout const` would 
just be the same as const.


That's not going to happen, of course. It would break everything. And 
I'm sure there are many obvious issues that I'm just ignoring here 
because the thing won't happen anyway. But it would make `inout foo` 
more symmetric.


[...]
The soundness of the function above seems good, but I don't know how to 
reason about the return type of f. Because mutable has no type modifier, 
it's hard to imagine doing this without one. And any time I think about 
how to define it, it breaks down. I can't imagine const(blah(T)) 
evaluating to mutable, no matter what blah is called, or how it works.


Literally the ONLY place this would be useful is for inout functions, 
and Andrei pretty much has declared that inout should be completely 
stricken from Phobos/druntime in favor of templates. I can't imagine any 
leeway for another type modifier.


const(inout) literally was an afterthought observation that we could 
relax the rules for this one case and get a little more usability.


Ok, I think we're in agreement. The described variant of inout would 
make sense, but isn't possible in D. It would be very awkward to 
implement the feature without changing how mutability qualifiers 
interact. And changing that would be way too disruptive. It's also not 
at all clear that it would go well with the rest of the language.


Re: Generic operator overloading for immutable types?

2017-06-13 Thread Steven Schveighoffer via Digitalmars-d-learn

On 6/13/17 7:51 PM, ag0aep6g wrote:

On 06/14/2017 12:45 AM, Steven Schveighoffer wrote:

No, the fact that immutable implicitly casts to const(inout) is a
special property enabled by the knowledge that immutable data can
NEVER change, so it's OK to assume it's (at least) const for all
references. The same cannot be true of const or mutable.


In other words: Immutable can't ever become mutable, at most it can
become const.

In the same vein: Mutable can't ever become immutable, at most it can
become const.

I don't see the fundamental difference. Mutable and immutable act very
much alike. They're just on opposing ends of the scale.


The fundamental difference is that const and immutable share a 
characteristic that mutable doesn't -- you can't mutate the data.


The reason const(inout) works is because const(immutable) evaluates to 
just immutable.


const() just evaluates to const. In this way, mutable is less 
"special" than immutable.



This cannot work, because g() has no idea what the true mutability of
x is. inout is not a template. This is why you can't implicitly cast
inout to anything except const, and you can't implicitly cast anything
to inout.


I don't follow. `inout const` doesn't need a template to do its thing.


Right, it's because the point at which the inout is "unwrapped" (i.e. at 
the point of return), it either becomes const or immutable. Inout 
functions can compile oblivious to how they will be called because the 
caller can completely determine the type that should be returned. It was 
the impetus to create inout in the first place -- why generate all these 
functions that do the same thing, just to change the return type, let 
the caller figure it out.



I'm not sure what you mean about implicit conversions. Obviously, an
inout result matches the corresponding inout argument(s). Doesn't matter
if that's an implicit conversion or whatever.


There is actually a difference. inout wraps one and exactly one type 
modifier. The table in that function shows what the modifier is 
depending on your collection of mutability parameters.


If ALL of them are the same, it becomes that same thing.

If any are different, you use the table to figure out. Most differences 
become const, some special cases become const(inout). This special case 
is solely to allow a function that takes both immutable and inout to 
potentially return immutable instead of just const.



Now, this code could be made to work:


bool condition;
auto f(inout int* x)
{
int* y; /* mutable now */
return condition ? x : y;
}
void main()
{
int* r1 = f(new int);
const r1 = f(new immutable int);
}


Mutable in, mutable out. Const in, const out. Immutable in, const out.
You can't get immutable out, because y is mutable, and it cannot become
immutable. Same principle as above. You never go from mutable to
immutable or the other way around, so everything's fine, no?


The soundness of the function above seems good, but I don't know how to 
reason about the return type of f. Because mutable has no type modifier, 
it's hard to imagine doing this without one. And any time I think about 
how to define it, it breaks down. I can't imagine const(blah(T)) 
evaluating to mutable, no matter what blah is called, or how it works.


Literally the ONLY place this would be useful is for inout functions, 
and Andrei pretty much has declared that inout should be completely 
stricken from Phobos/druntime in favor of templates. I can't imagine any 
leeway for another type modifier.


const(inout) literally was an afterthought observation that we could 
relax the rules for this one case and get a little more usability.


-Steve


Re: Generic operator overloading for immutable types?

2017-06-13 Thread ag0aep6g via Digitalmars-d-learn

On 06/14/2017 12:45 AM, Steven Schveighoffer wrote:
No, the fact that immutable implicitly casts to const(inout) is a 
special property enabled by the knowledge that immutable data can NEVER 
change, so it's OK to assume it's (at least) const for all references. 
The same cannot be true of const or mutable.


In other words: Immutable can't ever become mutable, at most it can 
become const.


In the same vein: Mutable can't ever become immutable, at most it can 
become const.


I don't see the fundamental difference. Mutable and immutable act very 
much alike. They're just on opposing ends of the scale.


This code doesn't work, and I see no way to make it work:


inout(int*) f(inout int* x, inout int* y)
{
return y;
}

auto g(inout int* x)
{
int* y;
return f(x, y);
}

void main()
{
int* x;
int* r1 = g(x);
}



This cannot work, because g() has no idea what the true mutability of x 
is. inout is not a template. This is why you can't implicitly cast inout 
to anything except const, and you can't implicitly cast anything to inout.


I don't follow. `inout const` doesn't need a template to do its thing. 
I'm not sure what you mean about implicit conversions. Obviously, an 
inout result matches the corresponding inout argument(s). Doesn't matter 
if that's an implicit conversion or whatever.


The goal is something that works just like `inout const`, but switching 
mutable and immutable.


I've realized that my example can be simplified:


bool condition;
auto f(inout int* x)
{
immutable int* y;
return condition ? x : y;
}
void main()
{
const r1 = f(new int);
immutable r2 = f(new immutable int);
}


That's `inout const` at work. Put a mutable or const int* in, you get a 
const int* out. Put immutable in, you get immutable out. You can't get 
mutable out, because the function might return y which is immutable, and 
it cannot become mutable.


Now, this code could be made to work:


bool condition;
auto f(inout int* x)
{
int* y; /* mutable now */
return condition ? x : y;
}
void main()
{
int* r1 = f(new int);
const r1 = f(new immutable int);
}


Mutable in, mutable out. Const in, const out. Immutable in, const out. 
You can't get immutable out, because y is mutable, and it cannot become 
immutable. Same principle as above. You never go from mutable to 
immutable or the other way around, so everything's fine, no?


Except, there's no type in D that has this meaning.


Re: Generic operator overloading for immutable types?

2017-06-13 Thread Steven Schveighoffer via Digitalmars-d-learn

On 6/13/17 5:58 PM, ag0aep6g wrote:

On 06/13/2017 10:50 PM, Steven Schveighoffer wrote:

const(inout) actually *is* a thing :)

It's a type constructor that can be implicitly cast from immutable.
This has advantages in some cases.

See (horribly written) table at the bottom if the inout function
section here: http://dlang.org/spec/function.html#inout-functions

Also I talked about it last year at Dconf 2016:
http://dconf.org/2016/talks/schveighoffer.html


Huh. There it is.

Took me some experimenting to understand what it does. If anyone else is
interested, this is where it clicked for me:


inout(int*) f(inout int* x, inout int* y)
{
return y;
}

inout(int*) g(inout int* x)
{
immutable int* y;
return f(x, y); /* Error: cannot implicitly convert expression f(x,
y) of type inout(const(int*)) to inout(int*) */
}


That code can't compile because g's inout return type says that it has
to return a mutable result for a mutable argument x. But y is immutable,
so that can't work.

We see in the error message that `f(x, y)` results in a `inout(const
int*)`. We can change g's return type to that (or `auto`) and the code
compiles. `inout const` enforces that the result is const or immutable.
It cannot be mutable.

This raises a question: There is no analogous inout variant that goes
the other way (to mutable and const but not immutable), is there?


No, the fact that immutable implicitly casts to const(inout) is a 
special property enabled by the knowledge that immutable data can NEVER 
change, so it's OK to assume it's (at least) const for all references. 
The same cannot be true of const or mutable.




This code doesn't work, and I see no way to make it work:


inout(int*) f(inout int* x, inout int* y)
{
return y;
}

auto g(inout int* x)
{
int* y;
return f(x, y);
}

void main()
{
int* x;
int* r1 = g(x);
}



This cannot work, because g() has no idea what the true mutability of x 
is. inout is not a template. This is why you can't implicitly cast inout 
to anything except const, and you can't implicitly cast anything to inout.


So it MUST return const(int *) (if you did auto r1 = g(x), typeof(r1) 
would be const(int *)).


-Steve


Re: Generic operator overloading for immutable types?

2017-06-13 Thread ag0aep6g via Digitalmars-d-learn

On 06/13/2017 10:50 PM, Steven Schveighoffer wrote:

const(inout) actually *is* a thing :)

It's a type constructor that can be implicitly cast from immutable. This 
has advantages in some cases.


See (horribly written) table at the bottom if the inout function section 
here: http://dlang.org/spec/function.html#inout-functions


Also I talked about it last year at Dconf 2016: 
http://dconf.org/2016/talks/schveighoffer.html


Huh. There it is.

Took me some experimenting to understand what it does. If anyone else is 
interested, this is where it clicked for me:



inout(int*) f(inout int* x, inout int* y)
{
return y;
}

inout(int*) g(inout int* x)
{
immutable int* y;
return f(x, y); /* Error: cannot implicitly convert expression f(x, 
y) of type inout(const(int*)) to inout(int*) */

}


That code can't compile because g's inout return type says that it has 
to return a mutable result for a mutable argument x. But y is immutable, 
so that can't work.


We see in the error message that `f(x, y)` results in a `inout(const 
int*)`. We can change g's return type to that (or `auto`) and the code 
compiles. `inout const` enforces that the result is const or immutable. 
It cannot be mutable.


This raises a question: There is no analogous inout variant that goes 
the other way (to mutable and const but not immutable), is there?


This code doesn't work, and I see no way to make it work:


inout(int*) f(inout int* x, inout int* y)
{
return y;
}

auto g(inout int* x)
{
int* y;
return f(x, y);
}

void main()
{
int* x;
int* r1 = g(x);
}



Re: Generic operator overloading for immutable types?

2017-06-13 Thread Steven Schveighoffer via Digitalmars-d-learn

On 6/13/17 3:58 PM, ag0aep6g wrote:

On 06/13/2017 09:29 PM, Gary Willoughby wrote:

Is it possible for the `result` variable in the following code to be
returned as an immutable type if it's created by adding two immutable
types?


Qualify the return type as `inout`:

inout(Rational) opBinary(/*...*/)(/*...*/) inout {/*...*/}

(That second `inout` is the same as the first one in your code.)

Then you get a mutable/const/immutable result when you call the method
on a mutable/const/immutable instance.

It doesn't matter a lot, though. `Rational` is a value type, so you can
convert the return value between qualifiers as you want, anyway. But it
affects what you get with `auto`, of course.


Yes exactly. I prefer personally to have value types always return 
mutable. You can always declare the result to be immutable or const, but 
declaring mutable is not as easy. e.g.:


immutable ReallyLongValueTypeName foo1();
ReallyLongValueTypeName foo2();

version(bad)
{
   auto x = foo1;
   ReallyLongValueTypeName y = foo1;
}
version(good)
{
   immutable x = foo2;
   auto y = foo2;
}



[...]

struct Rational
{
 public long numerator;
 public long denominator;

 public inout Rational opBinary(string op)(inout(Rational) other)
const


`inout` and `const` kinda clash here. Both apply to `this`. But
apparently, the compiler thinks `inout const` is a thing. No idea how it
behaves. I don't think it's a thing in the language. As far as I
understand, you should only put one of const/immutable/inout.


const(inout) actually *is* a thing :)

It's a type constructor that can be implicitly cast from immutable. This 
has advantages in some cases.


See (horribly written) table at the bottom if the inout function section 
here: http://dlang.org/spec/function.html#inout-functions


Also I talked about it last year at Dconf 2016: 
http://dconf.org/2016/talks/schveighoffer.html


However, I find it very surprising that inout and const can combine the 
way you have written it. Even though it does make logical sense. I'd 
suspect it would be a good candidate for a "you didn't really mean that" 
error from the compiler, but it might be hard to decipher from the 
parsed code.


-Steve


Re: Generic operator overloading for immutable types?

2017-06-13 Thread ag0aep6g via Digitalmars-d-learn

On 06/13/2017 09:29 PM, Gary Willoughby wrote:
Is it possible for the `result` variable in the following code to be 
returned as an immutable type if it's created by adding two immutable 
types?


Qualify the return type as `inout`:

inout(Rational) opBinary(/*...*/)(/*...*/) inout {/*...*/}

(That second `inout` is the same as the first one in your code.)

Then you get a mutable/const/immutable result when you call the method 
on a mutable/const/immutable instance.


It doesn't matter a lot, though. `Rational` is a value type, so you can 
convert the return value between qualifiers as you want, anyway. But it 
affects what you get with `auto`, of course.


[...]

struct Rational
{
 public long numerator;
 public long denominator;

 public inout Rational opBinary(string op)(inout(Rational) other) const


`inout` and `const` kinda clash here. Both apply to `this`. But 
apparently, the compiler thinks `inout const` is a thing. No idea how it 
behaves. I don't think it's a thing in the language. As far as I 
understand, you should only put one of const/immutable/inout.


Re: Generic operator overloading for immutable types?

2017-06-13 Thread Gary Willoughby via Digitalmars-d-learn
On Tuesday, 13 June 2017 at 11:36:45 UTC, Steven Schveighoffer 
wrote:


Nope, const works just fine. A clue is in your return type -- 
it's not inout!


This should work:

public Rational opBinary(string op)(Rational rhs) const

If Rational had any indirections, then inout would be required, 
and your signature wouldn't work (because you can't convert an 
inout(SomethingWithPointers) to SomethingWithPointers).


Why do I need to make the member function const, but not the 
parameter? Because the parameter is taken by value, 'this' is a 
reference.


-Steve


Is it possible for the `result` variable in the following code to 
be returned as an immutable type if it's created by adding two 
immutable types?


import std.stdio;

struct Rational
{
public long numerator;
public long denominator;

	public inout Rational opBinary(string op)(inout(Rational) other) 
const

{
static if (op == "+")
{
return inout(Rational)(0, 0);
}
else
{
static assert(0, "Operator '" ~ op ~ "' not 
implemented");
}
}
}

unittest
{
auto baz= immutable(Rational)(1, 3);
auto qux= immutable(Rational)(1, 6);
auto result = baz + qux;

writeln(typeof(result).stringof); // Result is not immutable!
}



Re: Generic operator overloading for immutable types?

2017-06-13 Thread Steven Schveighoffer via Digitalmars-d-learn

On 6/12/17 3:36 PM, H. S. Teoh via Digitalmars-d-learn wrote:

On Mon, Jun 12, 2017 at 07:38:44PM +, Gary Willoughby via 
Digitalmars-d-learn wrote:

In the following code is there any way to make the `opBinary` method
generic to be able to accept immutable as well as a standard type? The
code currently passes the unit test but I wonder if I could get rid of
the duplication to overload the operator? I'm failing badly.


This is what inout was designed for:

public inout Rational opBinary(string op)(inout Rational rhs)
{
static if (op == "+")
{
return inout(Rational)(0, 0);
}
else
{
static assert(0, "Operator '" ~ op ~ "' not 
implemented");
}
}

That should do the trick.


Nope, const works just fine. A clue is in your return type -- it's not 
inout!


This should work:

public Rational opBinary(string op)(Rational rhs) const

If Rational had any indirections, then inout would be required, and your 
signature wouldn't work (because you can't convert an 
inout(SomethingWithPointers) to SomethingWithPointers).


Why do I need to make the member function const, but not the parameter? 
Because the parameter is taken by value, 'this' is a reference.


-Steve


Re: Generic operator overloading for immutable types?

2017-06-13 Thread Steven Schveighoffer via Digitalmars-d-learn

On 6/12/17 3:51 PM, Gary Willoughby wrote:

I don't know how H. S. Teoh managed to answer 'before' I posted but
thanks guys! :)


D programmers are *that* good.

Seriously though, for NNTP connections, timestamp is taken from the 
submitter's PC.


-Steve


Re: Generic operator overloading for immutable types?

2017-06-13 Thread Gary Willoughby via Digitalmars-d-learn

On Monday, 12 June 2017 at 20:10:17 UTC, H. S. Teoh wrote:
Therefore, nowadays I always recommend writing parenthesis with 
type modifiers, so that the intent it unambiguous, i.e., always 
write `inout(Rational)` rather than `inout Rational`, unless 
you intend for `inout` to apply to the function rather than the 
return value. (And I apologize for the slip-up in my code 
example above, where I failed to do this for the function 
parameter.)



T


Ok, thanks.


Re: Generic operator overloading for immutable types?

2017-06-12 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, Jun 12, 2017 at 01:08:13PM -0700, Ali Çehreli via Digitalmars-d-learn 
wrote:
> On 06/12/2017 01:03 PM, Gary Willoughby wrote:
> > On Monday, 12 June 2017 at 19:36:52 UTC, H. S. Teoh wrote:
> 
> >>  public inout Rational opBinary(string op)(inout Rational rhs)
> 
> > Quick question about the signature, if I change it to (note the parens):
> >
> >public inout(Rational) opBinary(string op)(inout(Rational) rhs)
> >
> > It no longer works, why is that?
> 
> That's frequently faced issue with D. :) In the first declaration,
> inout applies to the member function.

More precisely, it applies to the `this` reference implicitly passed to
the member function.  You need inout to apply to `this`, otherwise
a.opBinary(b) won't work when a is an immutable instance.


> In the second one, it applies only to the return type. Walter has his
> rationale for doing it that way but it's confusing.
[...]

A few years ago we tried lobbying for the language to enforce putting
modifiers that only apply to the function itself on the far right side,
e.g.:

public Rational opBinary(string op)(inout Rational rhs) inout

But it was rejected for various reasons.

Therefore, nowadays I always recommend writing parenthesis with type
modifiers, so that the intent it unambiguous, i.e., always write
`inout(Rational)` rather than `inout Rational`, unless you intend for
`inout` to apply to the function rather than the return value. (And I
apologize for the slip-up in my code example above, where I failed to do
this for the function parameter.)


T

-- 
Маленькие детки - маленькие бедки.


Re: Generic operator overloading for immutable types?

2017-06-12 Thread Ali Çehreli via Digitalmars-d-learn

On 06/12/2017 01:03 PM, Gary Willoughby wrote:
> On Monday, 12 June 2017 at 19:36:52 UTC, H. S. Teoh wrote:

>>  public inout Rational opBinary(string op)(inout Rational rhs)

> Quick question about the signature, if I change it to (note the parens):
>
>public inout(Rational) opBinary(string op)(inout(Rational) rhs)
>
> It no longer works, why is that?

That's frequently faced issue with D. :) In the first declaration, inout 
applies to the member function. In the second one, it applies only to 
the return type. Walter has his rationale for doing it that way but it's 
confusing.


Ali



Re: Generic operator overloading for immutable types?

2017-06-12 Thread Gary Willoughby via Digitalmars-d-learn

On Monday, 12 June 2017 at 19:36:52 UTC, H. S. Teoh wrote:
On Mon, Jun 12, 2017 at 07:38:44PM +, Gary Willoughby via 
Digitalmars-d-learn wrote:
In the following code is there any way to make the `opBinary` 
method generic to be able to accept immutable as well as a 
standard type? The code currently passes the unit test but I 
wonder if I could get rid of the duplication to overload the 
operator? I'm failing badly.


This is what inout was designed for:

public inout Rational opBinary(string op)(inout Rational rhs)
{
static if (op == "+")
{
return inout(Rational)(0, 0);
}
else
{
static assert(0, "Operator '" ~ op ~ "' not 
implemented");
}
}

That should do the trick.


T


Quick question about the signature, if I change it to (note the 
parens):


   public inout(Rational) opBinary(string op)(inout(Rational) rhs)

It no longer works, why is that?


Re: Generic operator overloading for immutable types?

2017-06-12 Thread arturg via Digitalmars-d-learn

On Monday, 12 June 2017 at 19:51:37 UTC, Gary Willoughby wrote:
I don't know how H. S. Teoh managed to answer 'before' I posted 
but thanks guys! :)


might be a bug, happened here 
http://forum.dlang.org/post/ohbr5l$2mng$1...@digitalmars.com also.


Re: Generic operator overloading for immutable types?

2017-06-12 Thread Gary Willoughby via Digitalmars-d-learn
I don't know how H. S. Teoh managed to answer 'before' I posted 
but thanks guys! :)


Re: Generic operator overloading for immutable types?

2017-06-12 Thread ketmar via Digitalmars-d-learn

Gary Willoughby wrote:

In the following code is there any way to make the `opBinary` method 
generic to be able to accept immutable as well as a standard type? The 
code currently passes the unit test but I wonder if I could get rid of 
the duplication to overload the operator? I'm failing badly.


public inout(Rational) opBinary(string op)(inout(Rational) rhs) inout {
  static if (op == "+") {
return Rational(0, 0);
  } else {
static assert(0, "Operator '" ~ op ~ "' not implemented");
  }
}


Re: Generic operator overloading for immutable types?

2017-06-12 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, Jun 12, 2017 at 07:38:44PM +, Gary Willoughby via 
Digitalmars-d-learn wrote:
> In the following code is there any way to make the `opBinary` method
> generic to be able to accept immutable as well as a standard type? The
> code currently passes the unit test but I wonder if I could get rid of
> the duplication to overload the operator? I'm failing badly.

This is what inout was designed for:

public inout Rational opBinary(string op)(inout Rational rhs)
{
static if (op == "+")
{
return inout(Rational)(0, 0);
}
else
{
static assert(0, "Operator '" ~ op ~ "' not 
implemented");
}
}

That should do the trick.


T

-- 
Frank disagreement binds closer than feigned agreement.


Generic operator overloading for immutable types?

2017-06-12 Thread Gary Willoughby via Digitalmars-d-learn
In the following code is there any way to make the `opBinary` 
method generic to be able to accept immutable as well as a 
standard type? The code currently passes the unit test but I 
wonder if I could get rid of the duplication to overload the 
operator? I'm failing badly.



import std.stdio;

struct Rational
{
public long numerator;
public long denominator;

	public immutable Rational opBinary(string op)(immutable Rational 
rhs)

{
static if (op == "+")
{
return Rational(0, 0);
}
else
{
static assert(0, "Operator '" ~ op ~ "' not 
implemented");
}
}

public Rational opBinary(string op)(Rational rhs)
{
static if (op == "+")
{
return Rational(0, 0);
}
else
{
static assert(0, "Operator '" ~ op ~ "' not 
implemented");
}
}
}

unittest
{
auto foo = Rational(1, 3);
auto bar = Rational(1, 6);
writefln("%s", foo + bar);

auto baz = immutable Rational(1, 3);
auto qux = immutable Rational(1, 6);
writefln("%s", baz + qux);
}