May 22, 2018
On 2018-05-21 18:13:16 +0000, Ali ‡ehreli said:

> Templatized range types work well when they are used as template arguments themselves.
> 
> When you need to keep a single type like 'b' (i.e. b is not a template), and when you need to set a variable like mySubStream to a dynamic object, the solution is to use inputObject():
> ...

Thanks for the good example. The thing in my specific case is, that the streams are from a library, so no direct way to change their interface or so.

But anyway, the more background about the whole topic, the better.

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster

May 22, 2018
On 2018-05-21 18:55:36 +0000, Steven Schveighoffer said:

> So the issue here is that the lambda function inside myFunc is DIFFERENT than the one inside b. They are both the same function, but with essentially different names.

Aha... that explains it pretty good.

> When you use the alias, both are using the same exact lambda.

Ok. I didn't expect that the name is relevant in this case, instead assumed that only the types need to match.

> I see you are casting now as well,

Do I? Not that I'm aware of it in my pseudo-code example...


> What may make more sense (both for type sanity and for code reuse) is to wrap your call to filter into one place so it can be used wherever you need it:
> 
> auto wrapStream(S)(S str) { return str.filter!(x => x == myMessage); }
> 
> class b
> {
>     typeof(wrapStream(a.init.myStream)()) mySubStream;
> }
> 
> void myFunc() {
>     a myA = new a;
>     b myB = new b;
>     myB.mySubstream = myA.myStream.wrapStream;
> }

This would require one wrap function per different lambda, right? Assume I have 50-100 of these. Maybe the myMessage value can be given as parameter and with this becomes more like a "filter factory". Not sure if this would work.

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster

May 22, 2018
On 2018-05-21 20:17:04 +0000, Jonathan M Davis said:

> On Monday, May 21, 2018 16:05:00 Steven Schveighoffer via Digitalmars-d-
> learn wrote:
>> 
>> Well one thing that seems clear from this example -- we now have
>> __traits(isSame) to tell if lambdas are the same, but it looks like the
>> compiler doesn't subscribe to that belief...
>> 
>> https://run.dlang.io/is/FW3mVq
>> 
>> We should fix that...
> 
> Yeah. That part of lambdas has always been a problem. My guess here is that
> the problem stems from the fact that they're declared in separate scopes,
> but I don't know. Regardless of the reason for the failure though, that
> example really needs to work, or most anything that cares about lambdas
> being the same is going to have problems.

I think that's exactly the problem: I assumed that it's about the lambdas and associated types but would have never guessed that names, scope etc. play a role as well. Is this somewhere documented? Or at least a hint, would help a lot to be aware of this pitfall.

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster

May 22, 2018
On Tuesday, May 22, 2018 10:43:38 Robert M. Münch via Digitalmars-d-learn wrote:
> On 2018-05-21 20:17:04 +0000, Jonathan M Davis said:
> > On Monday, May 21, 2018 16:05:00 Steven Schveighoffer via Digitalmars-d-
> >
> > learn wrote:
> >> Well one thing that seems clear from this example -- we now have __traits(isSame) to tell if lambdas are the same, but it looks like the compiler doesn't subscribe to that belief...
> >>
> >> https://run.dlang.io/is/FW3mVq
> >>
> >> We should fix that...
> >
> > Yeah. That part of lambdas has always been a problem. My guess here is that the problem stems from the fact that they're declared in separate scopes, but I don't know. Regardless of the reason for the failure though, that example really needs to work, or most anything that cares about lambdas being the same is going to have problems.
>
> I think that's exactly the problem: I assumed that it's about the lambdas and associated types but would have never guessed that names, scope etc. play a role as well. Is this somewhere documented? Or at least a hint, would help a lot to be aware of this pitfall.

The issue is that you actually have different lambdas. They _look_ the same, but they aren't actually the same function. The compiler is not good about recognizing that two lambdas are identical. When Steven said that they had different names, he meant that it was like if you had declared:

int foo(int i)
{
    return i + 42;
}

int bar(int i)
{
    return i + 42;
}

Both functions are identical, but the compiler doesn't see that. It just looks at the signatures - and while everything about them is functionally equivalent, they have different names. Basically, the compiler is too dumb to figure out when two lambdas are actually identical. Some work has been done towards making it that smart, but there is still clearly more work to be done. As for how well any of this is documented, I don't know, but I suspect that at most, the spec has a line or two about it somewhere, especially since it's really not something that was planned for per se. It's just a natural fallout of how lambdas work and is surprisingly difficult to fix.

- Jonathan M Davis


May 22, 2018
On Tuesday, May 22, 2018 10:40:55 Robert M. Münch via Digitalmars-d-learn wrote:
> This would require one wrap function per different lambda, right? Assume I have 50-100 of these. Maybe the myMessage value can be given as parameter and with this becomes more like a "filter factory". Not sure if this would work.

Pretty much the only time that this sort of thing pops up is when you have to declare a variable that's a range (or some other similarly generic type) separately from where it's initialized. I'd expect that your application would have to be very large to have 50 - 100 instances of that. If you really were hitting it a lot, then maybe it would make sense to try and figure out a way to avoid having to declare a wrapper function, but in my experience, this sort of thing simply doesn't come up all that often. It's definitely an issue, and occasionally someone will come here and ask how to deal with it, but I'd be worried if it came up often enough that creating a wrapper function to deal with it was a problem.

The other way to fix the problem is to just call std.array.array on the range to get a dynamic array. It does mean allocating, but you run into fewer problems related to type inference, since you can then easily type the type rather than having to use type inference to get it.

- Jonathan M Davis


May 22, 2018
On 5/22/18 4:40 AM, Robert M. Münch wrote:
> On 2018-05-21 18:55:36 +0000, Steven Schveighoffer said:
> 
>> When you use the alias, both are using the same exact lambda.
> 
> Ok. I didn't expect that the name is relevant in this case, instead assumed that only the types need to match.

The type is the problem. The type returned by filter is parameterized on that *specific* lambda. If you look at the error message, it says something like "lamda1" and "lambda4" in the type for filter.

In order to make this work, the compiler would have to make the name of the lambda based on the actual AST inside it. I think something like that should be done.

> 
>> I see you are casting now as well,
> 
> Do I? Not that I'm aware of it in my pseudo-code example...

Haha, looking back, I see you didn't cast originally, which is probably the reason it didn't work :)

Here is the line from your revised example after Jonathan showed you how to declare a member of that type:

    myB.mySubstream = myA.myStream.filter!(x => x == myMessage);

And here is the subsequent line:

    myB.mySubstream = cast(myMessageType)myA.myStream.filter!(x => x == myMessage);

Both exactly the same, but one forces the cast. Your first line could have been done:

    myB.mySubstream = cast(typeof(myB.mySubstream))myA.myStream.filter!(x => x == myMessage);

Giving a name helps to make the code less verbose, but essentially that is what you are doing -- forcing the cast.

>> What may make more sense (both for type sanity and for code reuse) is to wrap your call to filter into one place so it can be used wherever you need it:
>>
>> auto wrapStream(S)(S str) { return str.filter!(x => x == myMessage); }
>>
>> class b
>> {
>>     typeof(wrapStream(a.init.myStream)()) mySubStream;
>> }
>>
>> void myFunc() {
>>     a myA = new a;
>>     b myB = new b;
>>     myB.mySubstream = myA.myStream.wrapStream;
>> }
> 
> This would require one wrap function per different lambda, right? Assume I have 50-100 of these. Maybe the myMessage value can be given as parameter and with this becomes more like a "filter factory". Not sure if this would work

Well, you then have to have 50-100 types of b with the correct member. Unless... you want to parameterize b, in which case it becomes REALLY easy:

class b(FilterType)
{
   FilterType mySubstream;
}

auto makeB(FilterType)(FilterType f)
{
   return new b!FilterType(f);
}

...

auto myB = myA.myStream.filter!(x => coolCrazyFunction(x)).makeB;

-Steve
1 2
Next ›   Last »