I was made aware of a feature of call_out that I didn't know about: if the object that the callback function belongs to is destructed before the call_out is supposed to be executed, the call_out is silently ignored.
But with Function.curry, this is not the case. Instead you get an error message.
Here is a demo program:
---cut here--- #!/usr/bin/env pike
class X { void work(int x) { werror("got work %d\n", x); } }
int main(int argc, array(string) argv) { X x = X(); call_out(x->work, 1, 1); if (argc > 1) call_out(destruct, 2, x); call_out(x->work, 3, 3); call_out(Function.curry(x->work)(4), 4); call_out(exit, 5, 0); return -1; } ---cut here---
Run it with no arguments to see what happens when the object isn't destroyed:
$ ./test.pike got work 1 got work 3 got work 4
Run it with any argument to trigger the destruction of x. Notise how you don't get any error for the 3-second callback, but the 4-second currified callback gives an error:
$ ./test.pike 1 got work 1 Cannot call functions in destructed objects. Unknown program: destructed object->function(4) server/pike/8.0.469/lib/modules/Function.pmod:85: Function.__lambda_65958_5_line_85() -:1: Pike.Backend(0)->`()(3600.0)
Would it make sense to have Function.curry also ignore calls to destructed objects? The change is trivial:
diff --git a/lib/modules/Function.pmod b/lib/modules/Function.pmod index 8a5498c483..d0b4a65335 100644 --- a/lib/modules/Function.pmod +++ b/lib/modules/Function.pmod @@ -83,7 +83,7 @@ function(mixed...:function(mixed...:mixed|void)) curry(function f) { return lambda(mixed ... args1) { return lambda(mixed ... args2) {
return f(@args1, @args2);
}; };return f && f(@args1, @args2);
}
In either case, I think this ought to be documented here: http://pike.lysator.liu.se/generated/manual/modref/ex/predef_3A_3A/Pike/__Ba...
Maybe that change was too trivial. Maybe it would be better to return UNDEFINED when f is false?
The most consistent behaviour would be if a Function.curry pointing to a destructed object was also destructed. But that is probably less trivial to implement...
Otherwise you have the reverse inconsistency that manually making an unguarded call to the curry would not throw an error even though an equivalent uncurried call would have. Getting a sudden "0" return may be inconsequential to call_out, but it might not be the case in all call sites.
Hm. Would it work to just implement `!, to "fool" the destructed test in the backend?
Marcus Comstedt (ACROSS) (Hail Ilpalazzo!) @ Pike (-) developers forum wrote:
Hm. Would it work to just implement `!, to "fool" the destructed test in the backend?
To chain that to the `! of the passed function argument? It would make sense to do that anyway, curry should be as transparent as possible.
I've tried, but either I'm doing something wrong, or it doesn't work. I think the destructed test is not that easily fooled. It seems to be this code that checks if a call_out is destructed:
check_destructed(Pike_sp - args); if(TYPEOF(Pike_sp[-args]) != T_INT)
It seems hard for any type of lambda or object to pass that test...
Anyhow, you have convinced me that my naive extension of Function.curry is a bad idea. If the curry object could be destructed, that would be nice, but I don't see how that can be implemented without a major effort.
For my application, it is easy enough to do like this:
function(mixed...:mixed) if_living(function(mixed...:mixed) f) { return lambda(mixed ...args) { return f && f(@args); }; }
And use it like:
Function.curry(if_living(some_method))(1, 2, 3)
instead of:
Function.curry(some_method)(1, 2, 3)
You just have to ensure that it is OK to return 0 if f is dead.
Hm. IS_DESTRUCTED (svalue.h) has a very complicated case for trampolines (which is what you get if returning a lambda referencing its environment), but it doesn't seem to be calling anything in the object no...
As for your "is_living" function, you could add an optional second argument to use as a different value than "0" as the fallback if you want to.
pike-devel@lists.lysator.liu.se