www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Function template declaration mystery...

reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
Hi, I'm lost reading some code:

A a;

auto do(alias f, A)(auto ref A _a){
	alias fun = unaryFun!f;
	return ...
	...
}

How is this alias stuff working? I mean what's the type of f? Is it an 
anonymous function which then gets checked to be unary? How is it 
recognized in the code using the function template?


This function can be called with code like this:

a.do((myType) {...myCode...});
do(a, (myType) {...myCode...});

What's wondering me here is that the template function only has one 
paraemter (_a) but I somehow can get my myCode into it. But the code 
looks like a parameter to me. So why isn't it like:

auto do(alias f, A)(auto ref A _a, ??? myCode){...

I'm a bit confused.

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster
Feb 28 2018
next sibling parent reply TheFlyingFiddle <none none.com> writes:
On Wednesday, 28 February 2018 at 17:47:22 UTC, Robert M. Münch 
wrote:
 Hi, I'm lost reading some code:

 A a;

 auto do(alias f, A)(auto ref A _a){
 	alias fun = unaryFun!f;
 	return ...
 	...
 }

 How is this alias stuff working? I mean what's the type of f? 
 Is it an anonymous function which then gets checked to be 
 unary? How is it recognized in the code using the function 
 template?


 This function can be called with code like this:

 a.do((myType) {...myCode...});
 do(a, (myType) {...myCode...});

 What's wondering me here is that the template function only has 
 one paraemter (_a) but I somehow can get my myCode into it. But 
 the code looks like a parameter to me. So why isn't it like:

 auto do(alias f, A)(auto ref A _a, ??? myCode){...

 I'm a bit confused.
Testing this with: auto foo(alias f, A)(auto ref A a) { return f(a); } I can call foo either like this: foo!(x => x + x)(1); or 1.foo!(x => x + x); but these will give errors foo(1, x => x + x); //Error 1.foo(x => x + x); // Error I don't see how you can get that kind of behavior...
Feb 28 2018
parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2018-02-28 18:01:50 +0000, TheFlyingFiddle said:
 
 Testing this with:
 
 auto foo(alias f, A)(auto ref A a) { return f(a); }
 
 I can call foo either like this:
 
 foo!(x => x + x)(1);
 or
 1.foo!(x => x + x);
 
 but these will give errors
 
 foo(1, x => x + x); //Error
 1.foo(x => x + x); // Error
 
 I don't see how you can get that kind of behavior...
Well, I stripped things trying to the core of where I'm struggeling. The 'a' in my code is a static and of course there is much more around going on. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Feb 28 2018
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Feb 28, 2018 at 06:47:22PM +0100, Robert M. Münch via
Digitalmars-d-learn wrote:
 Hi, I'm lost reading some code:
 
 A a;
 
 auto do(alias f, A)(auto ref A _a){
 	alias fun = unaryFun!f;
 	return ...
 	...
 }
 
 How is this alias stuff working? I mean what's the type of f? Is it an
 anonymous function which then gets checked to be unary? How is it
 recognized in the code using the function template?
Basically, the `alias f` is a catch-all template parameter that can bind to basically anything that has a symbol. It's typically used to bind to functions, delegates, and lambdas. Technically, the function is missing a sig constraint that verifies that f is in fact a unary function. So it will fail to compile if you pass something other than a unary function in.
 This function can be called with code like this:
 
 a.do((myType) {...myCode...});
Are you sure the function name is 'do'? Because that's a keyword, and I don't think it's a valid identifier.
 do(a, (myType) {...myCode...});
Are you sure this actually works? Is there another overload that takes a different parameter? The overload with `alias f`, AFAIK, can only be called with a compile-time parameter, like this: foo!((myType) { ... })(a); // or: a.foo!((myType) { ... });
 What's wondering me here is that the template function only has one
 paraemter (_a) but I somehow can get my myCode into it. But the code
 looks like a parameter to me. So why isn't it like:
 
 auto do(alias f, A)(auto ref A _a, ??? myCode){...
 
 I'm a bit confused.
[...] Are you sure there isn't an overload that takes a second parameter? Doesn't look like this will compile, given the above declaration. T -- Food and laptops don't mix.
Feb 28 2018
parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2018-02-28 18:09:41 +0000, H. S. Teoh said:

 Basically, the `alias f` is a catch-all template parameter that can
 bind to basically anything that has a symbol. It's typically used to
 bind to functions, delegates, and lambdas.
Aha... ok that makes it a bit more clear. So, if I have: auto myFunc(alias f1, alias f2, alias f3, A)(auto ref A _a){... I can use it like: a.myFunc( (myType) {...myCode...}, (myType) {...myCode...}, (myType) {...myCode...} ); and the aliases just continue to collect whatever comes inside the parameter specification. Does this idea hold? And it will give an error if there are to few/many aliases/parameters?
 Technically, the function is missing a sig constraint
How would that look like?
 that verifies thatf is in fact a unary function.  So it will fail to 
 compile if you pass
 something other than a unary function in.
Ok, that's clearn.
 This function can be called with code like this:
 
 a.do((myType) {...myCode...});
Are you sure the function name is 'do'? Because that's a keyword, and I don't think it's a valid identifier.
Well, it's just a bad picked word for my pseudo-code example... sorry.
 do(a, (myType) {...myCode...});
Are you sure this actually works? Is there another overload that takes a different parameter? The overload with `alias f`, AFAIK, can only be called with a compile-time parameter, like this: foo!((myType) { ... })(a); // or: a.foo!((myType) { ... });
I'm trying to better understand the D reactive framework rx: https://github.com/lempiji/rx This is the real code: osStream().filter!(wm => wm.message == WM_CREATE).doSubscribe((winMsg wm) {appInit();}); doSubscribe(osStream().filter!(wm => wm.message == WM_CREATE), (winMsg wm) {appInit();});
 What's wondering me here is that the template function only has one
 paraemter (_a) but I somehow can get my myCode into it. But the code
 looks like a parameter to me. So why isn't it like:
 
 auto do(alias f, A)(auto ref A _a, ??? myCode){...
 
 I'm a bit confused.
[...] Are you sure there isn't an overload that takes a second parameter? Doesn't look like this will compile, given the above declaration.
To be honste I'm not sure, I try to understand the code. It's the rx.oberservable file. Thanks a lot so far. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Feb 28 2018
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/28/18 12:47 PM, Robert M. Münch wrote:
 Hi, I'm lost reading some code:
 
 A a;
 
 auto do(alias f, A)(auto ref A _a){
      alias fun = unaryFun!f;
      return ...
      ...
 }
 
 How is this alias stuff working? I mean what's the type of f? Is it an 
 anonymous function which then gets checked to be unary? How is it 
 recognized in the code using the function template?
unaryFun is a template that returns a callable item. It could be a struct with an opCall, it could be a function template, it could be an alias to a real function, it could be a function pointer, delegate, etc. It also supports string lambdas, which existed before our current lambda syntax. i.e. alias f = unaryFun!"a - 5"; assert(f(10) == 5);
 
 
 This function can be called with code like this:
 
 a.do((myType) {...myCode...});
 do(a, (myType) {...myCode...});
 
 What's wondering me here is that the template function only has one 
 paraemter (_a) but I somehow can get my myCode into it. But the code 
 looks like a parameter to me. So why isn't it like:
 
 auto do(alias f, A)(auto ref A _a, ??? myCode){...
 
 I'm a bit confused.
This question is a little harder to understand. Perhaps you have real code that shows what you are confused about? -Steve
Feb 28 2018
parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2018-02-28 18:25:37 +0000, Steven Schveighoffer said:

 unaryFun is a template that returns a callable item.
That far I made it too :-)
 It could be a struct with an opCall, it could be a function template, 
 it could be an alias to a real function, it could be a function 
 pointer, delegate, etc.
As long as it's a function, makes sense.
 It also supports string lambdas, which existed before our current 
 lambda syntax.
 
 i.e. alias f = unaryFun!"a - 5";
 assert(f(10) == 5);
Yes, that's what the docs state. And I can imagin this. Bit this sentence is a bit hard to understand: "If fun is not a string, unaryFun aliases itself away to fun." Whatever this means.
 This question is a little harder to understand. Perhaps you have real 
 code that shows what you are confused about?
Ok, here it is: https://pastebin.com/tKACi488 See lines 81-84 for how I call it. And the problem I have is that doSubscribe returns "something" I'm not sure what I can do with. But if the scope ends, my subscription seems to be deleted and hence is not called when a message is coming in (see line: 118) And the question is now, what do I have to do that subscriptions that are done anywhere in the code survive the scope where they have been created? -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Feb 28 2018
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Feb 28, 2018 at 09:36:33PM +0100, Robert M. Münch via
Digitalmars-d-learn wrote:
[...]
 Yes, that's what the docs state. And I can imagin this. Bit this
 sentence is a bit hard to understand: "If fun is not a
 string, unaryFun aliases itself away to fun." Whatever this means.
[...] That means no overhead is introduced if `fun` is not a string. When `fun` is a string, unaryFun needs to create a lambda that implements the operation described in the string, and this may involve a GC allocation. If `fun` is already a function, delegate, or lambda, though, it can be used directly, so there's no need to incur the overhead of creating yet another delegate to wrap around `fun`. The way this is done is by unaryFun defining itself to be an alias to `fun` in this case, so that all references to unaryFun with that argument essentially becomes references to `fun` instead. It's really an implementation detail of Phobos that users don't really need to know, but I suppose the intent was to reassure the user that no unnecessary overhead will be incurred where it's avoidable. T -- If Java had true garbage collection, most programs would delete themselves upon execution. -- Robert Sewell
Feb 28 2018
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/28/18 3:36 PM, Robert M. Münch wrote:
 Yes, that's what the docs state. And I can imagin this. Bit this 
 sentence is a bit hard to understand: "If fun is not a string, unaryFun 
 aliases itself away to fun." Whatever this means.
It means that it simply becomes the alias you passed in. It means, there's no extra overhead in calling that unaryFun. But if you pass it something that needs some wrapping, it will do the wrapping.
 
 This question is a little harder to understand. Perhaps you have real 
 code that shows what you are confused about?
Ok, here it is: https://pastebin.com/tKACi488 See lines 81-84 for how I call it. And the problem I have is that doSubscribe returns "something" I'm not sure what I can do with. But if the scope ends, my subscription seems to be deleted and hence is not called when a message is coming in (see line: 118)
Looking through the rx package, it seems that it returns a class instance. That class implements the Disposable interface.
 And the question is now, what do I have to do that subscriptions that 
 are done anywhere in the code survive the scope where they have been 
 created?
I'm guessing from this observation that the class instance will unregister the subscription if it's destroyed, and because you aren't keeping a reference to that instance, the GC comes along and destroys it at some point. What I would recommend is you have to keep a reference to that class somewhere. Start out by just assigning it to a global. If that fixes your problem, I suggest you reach out to the author of rx, or read through the docs to see how you should properly use it, and where you need to keep that subscription (and for what purpose). If that *doesn't* fix your problem, then I'm not understanding what is happening, and you should definitely contact the rx author. File an issue in the github project. -Steve
Mar 01 2018
parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2018-03-01 12:01:19 +0000, Steven Schveighoffer said:

 Ok, here it is: https://pastebin.com/tKACi488
 
 See lines 81-84 for how I call it. And the problem I have is that 
 doSubscribe returns "something" I'm not sure what I can do with. But if 
 the scope ends, my subscription seems to be deleted and hence is not 
 called when a message is coming in (see line: 118)
 
Looking through the rx package, it seems that it returns a class instance. That class implements the Disposable interface.
Yes, AFAIU this can be kept around for reference. Seems to be intended for later access to the observers.
 I'm guessing from this observation that the class instance will 
 unregister the subscription if it's destroyed, and because you aren't 
 keeping a reference to that instance, the GC comes along and destroys 
 it at some point.
That was my guess too. But it's not the case. The unary is kept, hence I can just ignore the return value. What bite me here was a Windows effect. I subscribed my observer after the call to RegisterClass/CreateWindow. But Windows sometimes calls the window-procedure directly, without going through the message loop. And the messages I want to trigger on, are exactly ones that go directly. So, my code was never triggered because it just wasn't there. And after it was there, no more messages flow in. :-/ After rearranging things a bit it's working. But anyway, learned a lot while digging through the code and your feedback helped too. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Mar 01 2018