digitalmars.D.learn - auto & class members
- =?iso-8859-1?Q?Robert_M._M=FCnch?= (20/20) May 20 2018 I use the D RX lib [1] and can create a filtered stream using the auto k...
- Jonathan M Davis (20/37) May 20 2018 In cases like this, typeof is your friend. e.g. something like
- =?iso-8859-1?Q?Robert_M._M=FCnch?= (33/54) May 20 2018 Hi Jonathan, great! This got me a step further. So I can declare my
- =?iso-8859-1?Q?Robert_M._M=FCnch?= (18/41) May 20 2018 Answering myself: Using an alias helps.
- =?UTF-8?Q?Ali_=c3=87ehreli?= (33/35) May 21 2018 Templatized range types work well when they are used as template
- Jonathan M Davis (21/55) May 21 2018 Wow. Someone actually uses those? I don't think that I've ever seen anyo...
- =?iso-8859-1?Q?Robert_M._M=FCnch?= (9/16) May 22 2018 Thanks for the good example. The thing in my specific case is, that the
- Steven Schveighoffer (21/50) May 21 2018 So the issue here is that the lambda function inside myFunc is DIFFERENT...
- Jonathan M Davis (5/53) May 21 2018 That's basically what I was suggesting that he do, but I guess that I wa...
- Steven Schveighoffer (7/10) May 21 2018 Well one thing that seems clear from this example -- we now have
- Jonathan M Davis (8/16) May 21 2018 Yeah. That part of lambdas has always been a problem. My guess here is t...
- =?iso-8859-1?Q?Robert_M._M=FCnch?= (9/25) May 22 2018 I think that's exactly the problem: I assumed that it's about the
- Jonathan M Davis (25/46) May 22 2018 The issue is that you actually have different lambdas. They _look_ the s...
- =?iso-8859-1?Q?Robert_M._M=FCnch?= (13/34) May 22 2018 Ok. I didn't expect that the name is relevant in this case, instead
- Jonathan M Davis (17/21) May 22 2018 Pretty much the only time that this sort of thing pops up is when you ha...
- Steven Schveighoffer (34/65) May 22 2018 The type is the problem. The type returned by filter is parameterized on...
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/rxIn 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 20 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 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#inputRangeObjectWow. 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 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 5/20/18 1:46 PM, Robert M. Münch wrote:On 2018-05-20 17:40:39 +0000, Robert M. Münch 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. 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; } -SteveHi 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;
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:That's basically what I was suggesting that he do, but I guess that I wasn't clear enough. - Jonathan M DavisOn 2018-05-20 17:40:39 +0000, Robert M. Münch 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. 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; }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;
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: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 DavisThat'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...
May 21 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: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 | fasterWell 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.
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: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 DavisOn Monday, May 21, 2018 16:05:00 Steven Schveighoffer via Digitalmars-d- learn wrote: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.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.
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 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: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.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.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.I see you are casting now as well,Do I? Not that I'm aware of it in my pseudo-code example...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; -SteveWhat 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
May 22 2018