February 23, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Saturday, 23 February 2013 at 15:16:15 UTC, Steven Schveighoffer wrote:
> This actually is impossible to do with inout, unless your ranges are pointers or arrays (believe me, I tried with dcollections). But that is not inout's fault, it's because we have no way to specify tail-const arbitrary types.
>
I have read what come after, but clearly shouldn't have. This is the interesting thing to discuss. Can you expand on that ? Some code sample ?
|
February 23, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Saturday, 23 February 2013 at 06:06:46 UTC, deadalnix wrote:
> On Friday, 22 February 2013 at 18:55:58 UTC, kenji hara wrote:
>> No, const/inout overload does not intend to solve 'method hiding' problem.
>>
>> To clarify the situation, I try to explain.
>>
>> class A {
>> void foo() {}
>> void foo() const {}
>> }
>>
>
> You missed the important part. I'm talking about overload, not override. IE, not method hiding.
>
> What you have in class A here is useful for use cases that are now solved by inout (getting ranges/iterator from collection for instance).
To my mind the tradeoff underlying this whole issue is between the principle of Don't Repeat Yourself (i.e. by writing two identical functions), and the possible performance gains of a mechanism which permitted only one function and one vtable entry at runtime. But I think the latter are negligible. Making the vtable contain two entries instead of one seems negligible in terms of performance loss. If it is possible to have them point to the same function, then there will be no executable bloat. I'm pretty sure it's technically sound to allow this. I don't see why it would be hard to implement either.
So my thinking was, if 'inout' solves the DRY problem in normal code, but class inheritance makes it complicated when dealing with classes, why can't it just create one function and two different vtable entries which point to it?
|
February 23, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Sat, 23 Feb 2013 10:34:18 -0500, deadalnix <deadalnix@gmail.com> wrote:
> On Saturday, 23 February 2013 at 15:16:15 UTC, Steven Schveighoffer wrote:
>> This actually is impossible to do with inout, unless your ranges are pointers or arrays (believe me, I tried with dcollections). But that is not inout's fault, it's because we have no way to specify tail-const arbitrary types.
>>
>
> I have read what come after, but clearly shouldn't have. This is the interesting thing to discuss. Can you expand on that ? Some code sample ?
OK, here is a simple abstract example (the implementation details are not important, just the interface).
class Container(T)
{
...
struct range
{
...
@property inout(T) front() inout {...}
void popFront() {...}
bool empty() const { ...}
}
inout(range) opSlice() inout {...}
}
Nice, right? But it doesn't work, for one simple reason: popFront.
const Container!int cont = new Container!int(...);
auto r = cont[];
foreach(const x; r)
{
writeln(x);
}
doesn't work! typeof(r) is const(Container!(int).range), and since popFront is not const (and cannot be), you cannot iterate!
What you need is a SEPARATE "const_range" type (this is similar to C++). Then opSlice() const will return const_range.
And const range and range are not related, they are separate types. So there is no way to have inout choose the right one!
HOWEVER, if your range is a slice it works beautifully, because you can do a tail-inout slice inout(T)[].
Not possible with custom types.
I am working on a plan on how to fix this, because it really annoys me for my project so much to duplicate code that I just don't even support const ranges or immutable ranges! I do support immutable and const cursors, since those point at one element.
But I have not gotten around to pitching it because I need to come up with a really good solution :) Walter is so sour on any tail-const solution from past attempts that it has to be bullet-proof and easy.
-Steve
|
February 24, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Saturday, 23 February 2013 at 20:28:43 UTC, Steven Schveighoffer wrote: > On Sat, 23 Feb 2013 10:34:18 -0500, deadalnix <deadalnix@gmail.com> wrote: > >> On Saturday, 23 February 2013 at 15:16:15 UTC, Steven Schveighoffer wrote: >>> This actually is impossible to do with inout, unless your ranges are pointers or arrays (believe me, I tried with dcollections). But that is not inout's fault, it's because we have no way to specify tail-const arbitrary types. >>> >> >> I have read what come after, but clearly shouldn't have. This is the interesting thing to discuss. Can you expand on that ? Some code sample ? > > OK, here is a simple abstract example (the implementation details are not important, just the interface). > > class Container(T) > { > ... > struct range > { > ... > @property inout(T) front() inout {...} > void popFront() {...} > bool empty() const { ...} > } > > inout(range) opSlice() inout {...} > } > > Nice, right? But it doesn't work, for one simple reason: popFront. > > const Container!int cont = new Container!int(...); > > auto r = cont[]; > > foreach(const x; r) > { > writeln(x); > } > > doesn't work! typeof(r) is const(Container!(int).range), and since popFront is not const (and cannot be), you cannot iterate! > Obviously ! You need a mutable range of const elements, not a const range. > What you need is a SEPARATE "const_range" type (this is similar to C++). Then opSlice() const will return const_range. > In C++, const_iterator are iterator of const elements. They are not const themselves. > And const range and range are not related, they are separate types. So there is no way to have inout choose the right one! > They are indeed different types, but are exactly the same at the end except mangling. That is the real problem that have to be solved. |
February 24, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 02/23/2013 09:28 PM, Steven Schveighoffer wrote:
> ...
>
> But I have not gotten around to pitching it because I need to come up
> with a really good solution :) Walter is so sour on any tail-const
> solution from past attempts that it has to be bullet-proof and easy.
>...
Just add some form of generics and allow passing type constructors as generic arguments.
struct range[@type_constructor c]{
...
@property c(inout(T)) front() inout { ... }
void popFront() { ... }
bool empty() const { ... }
}
range![inout] opSlice() inout { ... }
|
February 25, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On Sunday, 24 February 2013 at 23:09:23 UTC, Timon Gehr wrote:
> On 02/23/2013 09:28 PM, Steven Schveighoffer wrote:
>> ...
>>
>> But I have not gotten around to pitching it because I need to come up
>> with a really good solution :) Walter is so sour on any tail-const
>> solution from past attempts that it has to be bullet-proof and easy.
>>...
>
> Just add some form of generics and allow passing type constructors as generic arguments.
>
> struct range[@type_constructor c]{
> ...
> @property c(inout(T)) front() inout { ... }
> void popFront() { ... }
> bool empty() const { ... }
> }
>
> range![inout] opSlice() inout { ... }
That isn't enough as the range!inout must become range!mutable or range!const or whatever after being returned.
|
February 25, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On 02/25/2013 05:27 PM, deadalnix wrote:
> On Sunday, 24 February 2013 at 23:09:23 UTC, Timon Gehr wrote:
>> On 02/23/2013 09:28 PM, Steven Schveighoffer wrote:
>>> ...
>>>
>>> But I have not gotten around to pitching it because I need to come up
>>> with a really good solution :) Walter is so sour on any tail-const
>>> solution from past attempts that it has to be bullet-proof and easy.
>>> ...
>>
>> Just add some form of generics and allow passing type constructors as
>> generic arguments.
>>
>> struct range[@type_constructor c]{
>> ...
>> @property c(inout(T)) front() inout { ... }
>> void popFront() { ... }
>> bool empty() const { ... }
>> }
>>
>> range![inout] opSlice() inout { ... }
>
> That isn't enough as the range!inout must become range!mutable or
> range!const or whatever after being returned.
The inout resolution of opSlice takes care of that.
|
February 25, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On Monday, 25 February 2013 at 16:35:52 UTC, Timon Gehr wrote:
> The inout resolution of opSlice takes care of that.
You got to be more precise about what you have in mind as I see plenty of way to make it fail (with static if for instance). I see also how it can work in trivials cases, but we need more than that.
|
February 25, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On 02/25/2013 05:45 PM, deadalnix wrote:
> On Monday, 25 February 2013 at 16:35:52 UTC, Timon Gehr wrote:
>> The inout resolution of opSlice takes care of that.
>
> You got to be more precise about what you have in mind as I see plenty
> of way to make it fail (with static if for instance). I see also how it
> can work in trivials cases, but we need more than that.
There is no question if it works, other languages have done a lot more than this. It is universal quantification. (using ad-hoc syntax elements. This is by no means how it should actually end up looking, in the unlikely case it is actually implemented.)
It can replace inout as follows:
int foo(inout int[] x) inout;
<=>
int foo[@type_constructor cons](cons int[] x) cons;
The benefit is that it allows multiple distinct type constructor variables, and that it works on aggregate declarations as well:
void foo[@type_constructor cons1, @type_constructor cons2](cons1 int[] x1, out cons1 int[] y1, cons2 int[] x2, cons2 int[] y2);
struct S[@type_constructor cons]{
cons(int)[] x;
}
I do not see a less powerful mechanism that addresses the problems that Steven wants to address.
|
February 25, 2013 Re: Purity, @safety, etc., in generic code | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On Mon, 25 Feb 2013 13:36:18 -0500, Timon Gehr <timon.gehr@gmx.ch> wrote:
> On 02/25/2013 05:45 PM, deadalnix wrote:
>> On Monday, 25 February 2013 at 16:35:52 UTC, Timon Gehr wrote:
>>> The inout resolution of opSlice takes care of that.
>>
>> You got to be more precise about what you have in mind as I see plenty
>> of way to make it fail (with static if for instance). I see also how it
>> can work in trivials cases, but we need more than that.
>
> There is no question if it works, other languages have done a lot more than this. It is universal quantification. (using ad-hoc syntax elements. This is by no means how it should actually end up looking, in the unlikely case it is actually implemented.)
>
> It can replace inout as follows:
>
> int foo(inout int[] x) inout;
>
> <=>
>
> int foo[@type_constructor cons](cons int[] x) cons;
>
> The benefit is that it allows multiple distinct type constructor variables, and that it works on aggregate declarations as well:
>
> void foo[@type_constructor cons1, @type_constructor cons2](cons1 int[] x1, out cons1 int[] y1, cons2 int[] x2, cons2 int[] y2);
>
> struct S[@type_constructor cons]{
> cons(int)[] x;
> }
>
> I do not see a less powerful mechanism that addresses the problems that Steven wants to address.
I actually proposed this to Walter a few years ago (same concept, different syntax). He said it was a solid proposal, and added too much complexity for the value.
What I am trying to come up with is a simplification. I will post sometime soon what I think will work. We definitely need something.
-Steve
|
Copyright © 1999-2021 by the D Language Foundation