Re: auto & class members

2018-05-22 Thread Steven Schveighoffer via Digitalmars-d-learn

On 5/22/18 4:40 AM, Robert M. Münch wrote:

On 2018-05-21 18:55:36 +, 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


Re: auto & class members

2018-05-22 Thread Jonathan M Davis via Digitalmars-d-learn
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




Re: auto & class members

2018-05-22 Thread Jonathan M Davis via Digitalmars-d-learn
On Tuesday, May 22, 2018 10:43:38 Robert M. Münch via Digitalmars-d-learn 
wrote:
> On 2018-05-21 20:17:04 +, 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




Re: auto & class members

2018-05-22 Thread Robert M. Münch via Digitalmars-d-learn

On 2018-05-21 18:55:36 +, 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



Re: auto & class members

2018-05-22 Thread Robert M. Münch via Digitalmars-d-learn

On 2018-05-21 20:17:04 +, 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



Re: auto & class members

2018-05-22 Thread Robert M. Münch via Digitalmars-d-learn

On 2018-05-21 18:13:16 +, 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



Re: auto & class members

2018-05-21 Thread Jonathan M Davis via Digitalmars-d-learn
On Monday, May 21, 2018 16:05:00 Steven Schveighoffer via Digitalmars-d-
learn wrote:
> On 5/21/18 3:22 PM, Jonathan M Davis wrote:
> > That's basically what I was suggesting that he do, but I guess that I
> > wasn't clear enough.
>
> 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.

- Jonathan M Davis



Re: auto & class members

2018-05-21 Thread Steven Schveighoffer via Digitalmars-d-learn

On 5/21/18 3:22 PM, Jonathan M Davis wrote:


That's basically what I was suggesting that he do, but I guess that I wasn't
clear enough.


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...

-Steve


Re: auto & class members

2018-05-21 Thread Jonathan M Davis via Digitalmars-d-learn
On Monday, May 21, 2018 11:13:16 Ali Çehreli via Digitalmars-d-learn wrote:
> On 05/20/2018 10:46 AM, Robert M. Münch wrote:
>  > But I still don't understand why I can't write things explicitly but
>  > have to use an alias for this.
>
> 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():
>
> import std.algorithm;
> import std.range;
>
> class a {
>  int[] myStream = [ 1, 2, 42, 100 ];
> }
>
>
> int myMessage = 42;
>
> class b {
>  InputRange!int mySubStream;
> }
>
> void myFunc() {
>  a myA = new a();
>  b myB = new b();
>
>  myB.mySubStream = inputRangeObject(myA.myStream.filter!(x => x ==
> myMessage));
>
>  assert(myB.mySubStream.equal([myMessage]));
> }
>
> void main() {
>  myFunc();
> }
>
> Now, mySubStream is a range variable that satisfies the input range
> interface and produces int elements. (Adjust accordingly.) You can use a
> more specialized range kind other than InputRange if the actual range
> supports it (e.g. ForwardRange!int, etc.):
>
>
> http://ddili.org/ders/d.en/ranges_more.html#ix_ranges_more.inputRangeObjec
> t
>
>https://dlang.org/phobos/std_range_interfaces.html#inputRangeObject

Wow. Someone actually uses those? I don't think that I've ever seen anyone
try except when they didn't understand ranges properly and thought that all
ranges derived from the interfaces in that module. I guess that they would
work in this case, but I think that the normal solution is to use typeof
(though as Robert here found, that can get a bit problematic when lambdas
get involved, whereas your solution here is pretty straightforward).

I'd be _very_ leery of using ForwardRange and the like though, since they're
going to have to allocate on every call to save, which gets expensive, and
far too often, range-based code doesn't call save correctly, meaning that
you'll often hit bugs using a forward range that's a class. Phobos is a
_lot_ better about it than it used to be, but I expect that there are still
a few such lingering bugs in there, and I'd expect the average range-based
code to screw it up. Really, the only way to get it right is to actually
test your code with reference type ranges.

If all you're using is a basic input range, then those interfaces just cost
you the one allocation and should be fine, but beyond that, I wouldn't
suggest using them if you can reasonably avoid it. And personally, I'd just
use Steven's solution of using a wrapper function so that you can ensure
that there's really only one lambda type involved, and typeof then works.

- Jonathan M Davis




Re: auto & class members

2018-05-21 Thread Jonathan M Davis via Digitalmars-d-learn
On Monday, May 21, 2018 14:55:36 Steven Schveighoffer via Digitalmars-d-
learn wrote:
> On 5/20/18 1:46 PM, Robert M. Münch wrote:
> > On 2018-05-20 17:40:39 +, Robert M. Münch said:
> >> Hi Jonathan, great! This got me a step further. So I can declare my
> >> member now. But I get an implict cast error when I try:
> >>
> >> class a {
> >> ... myStream;
> >> }
> >>
> >> class b {
> >> typeof(a.myStream.filter!(x => x == myMessage)) mySubStream;
> >> }
> >>
> >> void myFunc() {
> >> a myA = new a();
> >> b myB = new b();
> >>
> >> myB.mySubstream = myA.myStream.filter!(x => x == myMessage);
> >> }
> >>
> >> This gives (unnecessary stuff stripped):
> >>
> >> Error: cannot implicitly convert expression filter(...) of type
> >> app.myFunc.filter!(x => x == myMessage) to app.b.filter!(x => x ==
> >> myMessage)
> >
> > Answering myself: Using an alias helps.
> >
> > alias typeof(a.myStream.filter!(x => x == myMessage)) myMessageType;
>
> 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.
>
> When you use the alias, both are using the same exact lambda.
>
> I see you are casting now as well, which looks horrible to me -- if you
> change something in your lambda now you are in for some trouble.
>
> 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;
> }

That's basically what I was suggesting that he do, but I guess that I wasn't
clear enough.

- Jonathan M Davis




Re: auto & class members

2018-05-21 Thread Steven Schveighoffer via Digitalmars-d-learn

On 5/20/18 1:46 PM, Robert M. Münch wrote:

On 2018-05-20 17:40:39 +, Robert M. Münch said:

Hi Jonathan, great! This got me a step further. So I can declare my 
member now. But I get an implict cast error when I try:


class a {
... myStream;
}

class b {
typeof(a.myStream.filter!(x => x == myMessage)) mySubStream;
}

void myFunc() {
a myA = new a();
b myB = new b();

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

This gives (unnecessary stuff stripped):

Error: cannot implicitly convert expression filter(...) of type 
app.myFunc.filter!(x => x == myMessage) to app.b.filter!(x => x == 
myMessage)


Answering myself: Using an alias helps.

alias typeof(a.myStream.filter!(x => x == myMessage)) myMessageType;


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.


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

I see you are casting now as well, which looks horrible to me -- if you 
change something in your lambda now you are in for some trouble.


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;
}

-Steve


Re: auto & class members

2018-05-21 Thread Ali Çehreli via Digitalmars-d-learn

On 05/20/2018 10:46 AM, Robert M. Münch wrote:

> But I still don't understand why I can't write things explicitly but
> have to use an alias for this.

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():


import std.algorithm;
import std.range;

class a {
int[] myStream = [ 1, 2, 42, 100 ];
}


int myMessage = 42;

class b {
InputRange!int mySubStream;
}

void myFunc() {
a myA = new a();
b myB = new b();

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


assert(myB.mySubStream.equal([myMessage]));
}

void main() {
myFunc();
}

Now, mySubStream is a range variable that satisfies the input range 
interface and produces int elements. (Adjust accordingly.) You can use a 
more specialized range kind other than InputRange if the actual range 
supports it (e.g. ForwardRange!int, etc.):



http://ddili.org/ders/d.en/ranges_more.html#ix_ranges_more.inputRangeObject

  https://dlang.org/phobos/std_range_interfaces.html#inputRangeObject

Ali



Re: auto & class members

2018-05-20 Thread Robert M. Münch via Digitalmars-d-learn

On 2018-05-20 17:40:39 +, Robert M. Münch said:

Hi Jonathan, great! This got me a step further. So I can declare my 
member now. But I get an implict cast error when I try:


class a {
... myStream;
}

class b {
typeof(a.myStream.filter!(x => x == myMessage)) mySubStream;
}

void myFunc() {
a myA = new a();
b myB = new b();

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

This gives (unnecessary stuff stripped):

Error: cannot implicitly convert expression filter(...) of type 
app.myFunc.filter!(x => x == myMessage) to app.b.filter!(x => x == 
myMessage)


Answering myself: Using an alias helps.

alias typeof(a.myStream.filter!(x => x == myMessage)) myMessageType;

class b {
myMessageType mySubStream;
}

void myFunc() {
a myA = new a();
b myB = new b();

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

}

But I still don't understand why I can't write things explicitly but 
have to use an alias for this.


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



Re: auto & class members

2018-05-20 Thread Robert M. Münch via Digitalmars-d-learn

On 2018-05-20 14:49:59 +, Jonathan M Davis said:


In cases like this, typeof is your friend. e.g. something like

typeof(myStream.filter!(a => a == myMessage)) mySubStream;


Hi Jonathan, great! This got me a step further. So I can declare my 
member now. But I get an implict cast error when I try:


class a {
... myStream;
}

class b {
typeof(a.myStream.filter!(x => x == myMessage)) mySubStream;
}

void myFunc() {
a myA = new a();
b myB = new b();

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

This gives (unnecessary stuff stripped):

Error: cannot implicitly convert expression filter(...) of type 
app.myFunc.filter!(x => x == myMessage) to app.b.filter!(x => x == 
myMessage)


Why is myFunc now entering the game? I mean it's just the function 
containing the code. It seems that:


typeof(myA.myStream.filter!(x => x == myMessage))

and

typeof(a.myStream.filter!(x => x == myMessage)) are not the same.

But inside class b I can't use a specific variable instance. And in 
myFunc, I can't use a class type but need the specific instance.


Any further idea?



though you might have trouble with the lambda being subtly different type
even if you replace myMessage in it with something that will work in the
scope that mySubStream is being declared.


Not sure if the above problem is exactly what you mention here. This is 
all pretty tricky.




However, that could be fixed by
doing something like replacing the lambda with a free function or just
creating a function that returns what you want to assign to mySubStream.
Then you could just do something like

typeof(myHelperFunc(myStream)) mySubStream;

The exact solution can get a bit annoying in cases like this (it's arguably
the biggest downside to auto returns), but typeof does provide a way out if
you can get the expression to it to work. It is easier with local variables
than member variables though, since you don't necessarily have everything
you want to use in the expression that will give the variable its value
available at the point that the variable is declared - but that's why
a helper function can help, since it provides a way to encapsulate the
expression and reuse it between the assignment and the declaration.


Not sure I understand every aspect but it's getting clearer... Thanks so far.

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



Re: auto & class members

2018-05-20 Thread Jonathan M Davis via Digitalmars-d-learn
On Sunday, May 20, 2018 16:30:10 Robert M. Münch via Digitalmars-d-learn 
wrote:
> I use the D RX lib [1] and can create a filtered stream using the auto
> keyword:
>
> struct a {
>   SubjectObject!myType myStream;
>   ??? mySubStream;
> }
>
> void myfunc(){
>   a myA = new a();
>
>   auto mySubStream = a.myStream.filter!(a => a == myMessage);
>   ...
> }
>
> The problem is, that I don't find out what the type of mySubStream is,
> which I would like to make a member of the struct, so that I can
> reference it outside the function too.
>
> How can I find out the type of an auto? This here seems to be a pretty
> complicated templated, nested type, whatever result.
>
> [1] https://github.com/lempiji/rx

In cases like this, typeof is your friend. e.g. something like

typeof(myStream.filter!(a => a == myMessage)) mySubStream;

though you might have trouble with the lambda being subtly different type
even if you replace myMessage in it with something that will work in the
scope that mySubStream is being declared. However, that could be fixed by
doing something like replacing the lambda with a free function or just
creating a function that returns what you want to assign to mySubStream.
Then you could just do something like

typeof(myHelperFunc(myStream)) mySubStream;

The exact solution can get a bit annoying in cases like this (it's arguably
the biggest downside to auto returns), but typeof does provide a way out if
you can get the expression to it to work. It is easier with local variables
than member variables though, since you don't necessarily have everything
you want to use in the expression that will give the variable its value
available at the point that the variable is declared - but that's why
a helper function can help, since it provides a way to encapsulate the
expression and reuse it between the assignment and the declaration.

- Jonathan M Davis