March 10, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7543



--- Comment #10 from Steven Schveighoffer <schveiguy@yahoo.com> 2012-03-09 16:07:27 PST ---
(In reply to comment #8)

> Indeed.  The point is that there are two possible interpretations of the opApply signature, and the spec isn't clear on which applies:
> 
> (a) the constancy is passed through to the delegate
> (b) the delegate has an inout parameter in its own right

I think it must be (b).  Consider you don't know when the delegate was
constructed:

void foo(inout(int)* x, inout(int)* delegate(inout(int)* x) dg)
{
   inout(int)* bar(inout(int)* m) { return m;}
   auto dg2 = &bar;
   assert(typeof(dg2) == typeof(dg)); // ???

   immutable int y = 5;
   dg2(&y); // ok
   dg(&y); // must fail!
}

If the assert doesn't pass, then what is the type of dg vs. dg2?  If it passes, then dg and dg2 are interchangeable, and you will violate const (what if x is mutable?).  Even if the assert fails, it's going to be way *way* too confusing to have two types that are identical in syntax be actually different types under the hood.

We *absolutely* need a new syntax if case (a) is to be included.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 10, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7543



--- Comment #11 from timon.gehr@gmx.ch 2012-03-09 16:16:14 PST ---
(In reply to comment #10)
> We *absolutely* need a new syntax if case (a) is to be included.

I agree.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 10, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7543



--- Comment #12 from Boscop <kingboscop@gmail.com> 2012-03-09 16:21:28 PST ---
(In reply to comment #7)
> (In reply to comment #6)
> > (In reply to comment #5)
> > > (In reply to comment #4)
> > 
> > You can't do:
> > A delegate(inout(A)) dg = (immutable(A) a) => new B(a);
> 
> You claimed I could.
> 
> Boskop wrote:
> > B delegate(immutable(A)) <: A delegate(inout(A))

This is wrong, I didn't realize that I accidentally wrote that, sorry. What I wrote above this (and in comment 6) was right:

Given R2 <: R1,
R2 delegate(inout(T)) <: R1 delegate(immutable(T))
(because of argument contravariance and immutable(T) <: inout(T)).

I didn't know at first that you wanted to point this out (probably was too distracted by your code example).

(In reply to comment #8)
> I think the claim that the reporter is actually making is that the inout constancy of the delegate parameter should vary with that of this.  In other words,
> 
>     int opApply(int delegate(ref inout(int)) dg) inout
> 
> should be callable as if it's any one of these:
> 
>     int opApply(int delegate(ref int) dg)
>     int opApply(int delegate(ref const(int)) dg) const
>     int opApply(int delegate(ref immutable(int)) dg) immutable

The problem is more deep-rooted. You can only pass delegates to opApply that
take a supertype of "ref inout(int)" as argument, but immutable(T) <: inout(T)
and const(T) <: inout(T), and T <: inout(T).

If it were possible to use inout that way, you would have to enable implicit
casts from T to immutable(T) (T <: immutable(T)) but you can't do that because
functions taking an immutable arg assume that it's not changed outside!
There is no way to have inout delegates like you want without enabling implicit
casts from T to immutable(T)!

But we don't need to enable implicit casts from T to immutable(T).

We can have our cake and eat it, too!

Now this is the important thing to notice:
In the case with opApply you actually want to transfer the constness of the
this object to the argument to dg!

foreach (e; new immutable(C)([1,2,3])) opApply takes ONLY a "int opApply(int
delegate(ref immutable(int)) dg) immutable"
foreach (e; new const(C)([1,2,3])) opApply takes ONLY a "int opApply(int
delegate(ref const(int)) dg) const"
foreach (e; new C([1,2,3])) opApply takes ONLY a "int opApply(int delegate(ref
int) dg)"

So we need a way to transfer the constness of the this object to declarations within the class. This is basically inout for types (inout_t). (It's related to the inout that is written after method signatures, but can now be be transferred to arbitrary declarations).

So, with inout_t you would write:
class C {
    int[] arr;
    this(int[] a){arr = a;}
    int opApply(int delegate(ref inout_t(int)) dg) inout {
        foreach(ref e; arr)
            if(auto r = dg(e)) return r;
        return 0;
    }
}

BTW: If D didn't have transitive const, you could implement it with inout_t
like this:
struct S{inout_t(S)* next;}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 10, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7543



--- Comment #13 from timon.gehr@gmx.ch 2012-03-09 16:36:19 PST ---
(In reply to comment #12)
> 
> I didn't know at first that you wanted to point this out (probably was too distracted by your code example).
> 

It seems to me in general that it could be beneficial to the quality of your contributions if you would spend more time reading and less time writing. ;)

> 
> So, with inout_t you would write:
> class C {
>     int[] arr;
>     this(int[] a){arr = a;}
>     int opApply(int delegate(ref inout_t(int)) dg) inout {
>         foreach(ref e; arr)
>             if(auto r = dg(e)) return r;
>         return 0;
>     }
> }
> 

This is an interesting suggestion. There are some other ideas discussed here: http://forum.dlang.org/post/jhr0t6$24v6$1@digitalmars.com

> BTW: If D didn't have transitive const, you could implement it with inout_t
> like this:
> struct S{inout_t(S)* next;}

This is moot.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 10, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7543



--- Comment #14 from Stewart Gordon <smjg@iname.com> 2012-03-10 03:45:21 PST ---
(In reply to comment #10)
> void foo(inout(int)* x, inout(int)* delegate(inout(int)* x) dg)
> {
>    inout(int)* bar(inout(int)* m) { return m;}
>    auto dg2 = &bar;
>    assert(typeof(dg2) == typeof(dg)); // ???

Whether we pick (a) or (b), it should apply equally to declarations in the body
of foo as to parameters of foo itself.  So this assert would pass either way.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
March 10, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7543



--- Comment #15 from Stewart Gordon <smjg@iname.com> 2012-03-10 04:00:38 PST ---
(In reply to comment #10)
> Even if the assert fails, it's going to be way *way* too confusing
> to have two types that are identical in syntax be actually different types
> under the hood.

Following on from my last comment, I see now that it reinforces why (b) is better as the default behaviour.  If we went with (a), removing the inouts from the signature of foo would completely change the meaning of the inouts in bar.

What (b) is saying is that inout is treated as referencing the constancy level with which the function is called _only_ if it would be illegal in the absence of an enclosing inout context.

> We *absolutely* need a new syntax if case (a) is to be included.

Would we allow it in the body of bar as well, for when we want to reference the constancy level with which foo was called?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
1 2
Next ›   Last »