Jump to page: 1 2
Thread overview
auto & class members
May 20, 2018
Robert M. Münch
May 20, 2018
Jonathan M Davis
May 20, 2018
Robert M. Münch
May 20, 2018
Robert M. Münch
May 21, 2018
Ali Çehreli
May 21, 2018
Jonathan M Davis
May 22, 2018
Robert M. Münch
May 21, 2018
Jonathan M Davis
May 21, 2018
Jonathan M Davis
May 22, 2018
Robert M. Münch
May 22, 2018
Jonathan M Davis
May 22, 2018
Robert M. Münch
May 22, 2018
Jonathan M Davis
May 20, 2018
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

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

May 20, 2018
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


May 20, 2018
On 2018-05-20 14:49:59 +0000, 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

May 20, 2018
On 2018-05-20 17:40:39 +0000, 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

May 21, 2018
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

May 21, 2018
On 5/20/18 1:46 PM, Robert M. Münch wrote:
> On 2018-05-20 17:40:39 +0000, 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
May 21, 2018
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 +0000, 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


May 21, 2018
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


May 21, 2018
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
May 21, 2018
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

« First   ‹ Prev
1 2