May 01, 2012
On 05/01/2012 04:02 AM, Mehrdad wrote:
> Wha..?!
>
> I can't believe delegates work so poorly in D...

It is not the delegates, it is the type deduction algorithm for implicit function template instantiation. The issue is that the parameters do not cross-talk, which means R is not known during matching: How to detect the argument type from bool delegate(ElementType!R) alone if R is not known? The obvious solution would be to use an actual inference algorithm that uses information that can be gained from other parameters as well as information already known from the current parameter.

You might want to file an enhancement request, because this exact thing seems to trip up many programmers. (i.e. it would be a lot more intuitive and convenient if it worked.)


> they're practically
> unusable unless you're passing them as template parameters (which brings
> on its own set of bugs)...

I haven't encountered those so far.

>
> Seems like every time I try to escape a bug somehow, another one pops up :(
>

The situation is improving. Furthermore, there is a very large subset of the language that is already very usable.

Another way to make your code compile:

private import std.range;
void filter(R,S)(R, bool delegate(S)) if(is(S==ElementType!R)){ }
void main() { [1, 2, 3].filter((int x) { return x < 3; }); }
May 01, 2012
I guess the problem is with type deduction, but the trouble is that half the reason why type deduction is useful is in the case of lambdas. They become effectively useless if you have to type out their entire signature, since you could then just make them a separate function (or just use a mixin instead, to create a closure... which I've done before).

This isn't really an 'enhancement', since the error message is clearly a bug. So I filed them both as bugs.

http://d.puremagic.com/issues/show_bug.cgi?id=8009
http://d.puremagic.com/issues/show_bug.cgi?id=8010

The other bug I was referring to was something along the lines of the compiler telling me, "I can't really handle closures as template parameters very well (something about 'local variable' or whatever), so I'll just give you an obscure error every now and then".
It only happened when it was inside another function, and some conditions were satisfied. (I don't remember the exact message, but when I searched it, I saw it had been already reported before...)


Oh and thanks for the alternative, it's a good workaround to know. :)

The trouble is that while it solves the bug I posted, it's still not solving my (actual) problem.
The actual problem was that I wanted to do something along the lines of:

private import std.range;
auto filter(R, F)(R r, F f) { /*return a filtered range*/ }
void main() { [1, 2, 3].filter(x => x < 3); }

but I resorted to 'delegates' because this didn't work.
However, when I did so, I decided to use a delegate anyway, while using 'scope' on the delegate and its parameter to avoid a heap allocation, but that didn't work either...
so I just filed the bug report with a case that had less type inference. But the trouble is that the caller needing to say "delegate" is *already* too much typing, so the rest don't really help. :\


On 05/01/2012 04:02 AM, Mehrdad wrote:
> Wha..?!
>
> I can't believe delegates work so poorly in D...

It is not the delegates, it is the type deduction algorithm for implicit
function template instantiation. The issue is that the parameters do not
cross-talk, which means R is not known during matching: How to detect
the argument type from bool delegate(ElementType!R) alone if R is not
known? The obvious solution would be to use an actual inference
algorithm that uses information that can be gained from other parameters
as well as information already known from the current parameter.

You might want to file an enhancement request, because this exact thing
seems to trip up many programmers. (i.e. it would be a lot more
intuitive and convenient if it worked.)


> they're practically
> unusable unless you're passing them as template parameters (which brings
> on its own set of bugs)...

I haven't encountered those so far.

>
> Seems like every time I try to escape a bug somehow, another one pops up :(
>

The situation is improving. Furthermore, there is a very large subset of
the language that is already very usable.

Another way to make your code compile:

private import std.range;
void filter(R,S)(R, bool delegate(S)) if(is(S==ElementType!R)){ }
void main() { [1, 2, 3].filter((int x) { return x < 3; }); } 

May 01, 2012
On 01/05/2012 02:00, Mehrdad wrote:
> Some ICE for y'all.
>
> void filter(R)(scope bool delegate(ref BAD!R) func) { }
> void main() { filter(r => r); }

Is this in bugzilla? It can't get fixed if no one knows about it! (Make sure to give it the ice keyword once you've submitted so it gets bumped to the top of the queue!)

-- 
Robert
http://octarineparrot.com/
May 01, 2012
On 05/01/2012 01:20 PM, Mehrdad wrote:
> I guess the problem is with type deduction, but the trouble is that half
> the reason why type deduction is useful is in the case of lambdas. They
> become effectively useless if you have to type out their entire
> signature, since you could then just make them a separate function (or
> just use a mixin instead, to create a closure... which I've done before).
>
> This isn't really an 'enhancement', since the error message is clearly a
> bug. So I filed them both as bugs.
>
> http://d.puremagic.com/issues/show_bug.cgi?id=8009
> http://d.puremagic.com/issues/show_bug.cgi?id=8010
>
> The other bug I was referring to was something along the lines of the
> compiler telling me, "I can't really handle closures as template
> parameters very well (something about 'local variable' or whatever), so
> I'll just give you an obscure error every now and then".
> It only happened when it was inside another function, and some
> conditions were satisfied. (I don't remember the exact message, but when
> I searched it, I saw it had been already reported before...)
>

Probably you mean this one:

struct S{
    int x;
    auto foo(alias a)(){return a(x);}
}

void main(){
    auto s = S(2);
    int y = 3;
    writeln(s.foo!(x=>x+y)());
}

Error: template instance foo!(__lambda2) cannot use local '__lambda2(__T1)' as parameter to non-global template foo(alias a)

This is an arbitrary restriction and should be fixed. The 'this' pointer for 'foo' could be passed in a reserved slot at the beginning of the context for 'main'.


>
> Oh and thanks for the alternative, it's a good workaround to know. :)
>
> The trouble is that while it solves the bug I posted, it's still not
> solving my (actual) problem.
> The actual problem was that I wanted to do something along the lines of:
>
> private import std.range;
> auto filter(R, F)(R r, F f) { /*return a filtered range*/ }
> void main() { [1, 2, 3].filter(x => x < 3); }
>

private import std.range;
auto filter(R, F)(R r, F f) { /*return a filtered range*/ }
void main() { [1, 2, 3].filter((int x) => x < 3); }
May 02, 2012
Yeah I just posted it yesterday.
http://d.puremagic.com/issues/show_bug.cgi?id=8009

"Robert Clipsham"  wrote in message news:jnoi6g$1f79$1@digitalmars.com... 

On 01/05/2012 02:00, Mehrdad wrote:
> Some ICE for y'all.
>
> void filter(R)(scope bool delegate(ref BAD!R) func) { }
> void main() { filter(r => r); }

Is this in bugzilla? It can't get fixed if no one knows about it! (Make sure to give it the ice keyword once you've submitted so it gets bumped to the top of the queue!)

-- 
Robert
http://octarineparrot.com/
May 02, 2012
Let's say you have this hypothetical piece of code:

interface IConnection { string send(string data); }
class Student
{
   private string id;
   private string cachedName;
   private IConnection conn;

   public this(string id) { this.id = id; this.conn = ...; }

   public @property string name() const
   {
       if (!cachedName)
       { cachedName = conn.send("get_name: " ~ id); }
       return cachedName;
   }
}
void main()
{
   auto student = new immutable(Student)("142341234");
   writeln(student.name);
}


Notice that there are two const-related issues in the code that are literally *unsolvable* (unless you avoid const/immutable entirely, or unless you cast() -- but then your entire code would be filled with dangerous const casts...).

How does D2 plan to address these issues in the 'ideal' implementation? 

May 02, 2012
More problems... similar, but this time related to templates.

 struct Filter(R) { this(R) { } }
 template filter(R) { alias Filter!(R).__ctor filter; }
 void main() { filter([1, 2, 3]); }

Error: template linq.filter(R) is not a function template


Why?
May 02, 2012
Yes, that non-global issue was the exact issue I was referring to. It drives me nuts whenever I try to give in and use templates.


Regarding your "fix":
Is it *really* intended that user should say
  arr.filter((typeof(some_random_expression) x) => x < y);
instead of
  arr.filter(x => x < y);
?

I think it's pretty obvious why that doesn't really work in practice... 

May 02, 2012
On Tuesday, May 01, 2012 18:55:03 Mehrdad wrote:
> Let's say you have this hypothetical piece of code:
> 
> interface IConnection { string send(string data); }
> class Student
> {
>     private string id;
>     private string cachedName;
>     private IConnection conn;
> 
>     public this(string id) { this.id = id; this.conn = ...; }
> 
>     public @property string name() const
>     {
>         if (!cachedName)
>         { cachedName = conn.send("get_name: " ~ id); }
>         return cachedName;
>     }
> }
> void main()
> {
>     auto student = new immutable(Student)("142341234");
>     writeln(student.name);
> }
> 
> 
> Notice that there are two const-related issues in the code that are literally *unsolvable* (unless you avoid const/immutable entirely, or unless you cast() -- but then your entire code would be filled with dangerous const casts...).
> 
> How does D2 plan to address these issues in the 'ideal' implementation?

Since casting away const and mutating something would break the const system (with it being even worse with immutable), you can never used a cached value in a const function like this. The closest that you would be able to do would be to have a const and non-const version where the non-const versioned cached the value, and the const version didn't (it would likely use the value if already cached, but it would never cache it). And in the case of immutable, you should probably have a separate constructor which actually sets everything. So, something like

interface IConnection { string send(string data); }
class Student
{
    private string id;
    private string cachedName;
    private IConnection conn;

    public this(string id) { this.id = id; this.conn = ...; }

    public this(string id) immutable
    {
        this.id = id;
        this.conn = ...;
        cachedName = conn.send("get_name: " ~ id);
    }

    public @property string name()
    {
        if(!cachedName)
            cachedName = conn.send("get_name: " ~ id);

        return cachedName;
    }

    public @property string name() const
    {
        return cachedName is null ? conn.send("get_name: " ~ id)
                                  : cachedName;
    }
}

void main()
{
    auto student = new immutable(Student)("142341234");
    writeln(student.name);
}


Now, if you don't care about purity at all, you can have an external hash of some kind which holds your cached values. e.g.

public @property string name() const
{
    if(auto cachedName = id in globalCache)
        return cachedName;
    else
    {
        auto cachedName = conn.send("get_name: " ~ id);
        globalCache[id] = cachedName;
        return cachedName;
    }
}


But that's not exactly a pleasant solution, since the state is no longer in the object where it belongs. The first would likely be the better way to do it. However, some folks that like lazy loading such as this seem to have come  to the conclusion that they should just pretty much never use const, because it's too restrictive for they want to do.

- Jonathan M Davis


P.S. You really should create new threads rather than resurrecting threads that are months old. It becomes very easy to ignore them when I have to scroll way up to find a new post in the threaded view. The only reason that I don't miss them entirely is the fact that my e-mail client tells me how many e-mails I have which are unread.
May 02, 2012
Mehrdad:

> Notice that there are two const-related issues in the code that are literally *unsolvable* (unless you avoid const/immutable entirely, or unless you cast() -- but then your entire code would be filled with dangerous const casts...).

Casting away casts is rather dangerous in D (more than in C++), I
kind of never do it. So just don't use const/immutable in such
cases.

Bye,
bearophile