Sigh. Must we? This kind of large-scale dynamic behaviour tends to go wrong even in languages that provide type-checking and sensible lexical scoping. In PHP it's basically just a recipe for the kinds of untouchable "black magic" code that haunt code bases. It's basically some nasty syntax wrapped around even nastier GOTO-style behaviour.
"is_callable" is no better, because if you don't know, you shouldn't even be trying it. It might as well be called "does_stick", because you're basically just throwing spaghetti (code) at a wall.
Having a blog about programming languages and formal methods I understand the basis of your disgust with PHP. In a perfect world all programming languages would use robust type systems and be capable of formal verification. PHP makes no attempt at either.
Out in user land there are all sorts of scenarios where dynamic invocation makes sense. Higher-order functions are the most obvious. Is mapping strtolower on an array of strings really spaghetti code?
Most uses for dynamic invocation are at the utility/framework level, not in application land. You're right, it's dangerous when used improperly, but you should consider coming down from the theory clouds if you believe dynamic invocation and goto are in the same boat on the river styx.
> Having a blog about programming languages and formal methods I understand the basis of your disgust with PHP. In a perfect world all programming languages would use robust type systems and be capable of formal verification. PHP makes no attempt at either.
Way to straw-man, dude! Nowhere did I suggest anything about perfect worlds or what programming languages should be like. I said that this particular feature of this particular language is undesirable because it introduces unnecessary complexity and risky levels of unpredictability into code that probably doesn't need to use it.
> Out in user land there are all sorts of scenarios where dynamic invocation makes sense. Higher-order functions are the most obvious. Is mapping strtolower on an array of strings really spaghetti code?
What does "dynamic invocation" have to do with higher-order functions? Pretty much everything described in the article deals only with functions as first-class values and unrestricted indirection. There is nothing "higher order" about them. If you're talking about implementing higher-order functions, then the only requirement is the ability to pass around functions as values. There is certainly no requirement for indirection on strings!
If functions are first-class values, that's absolutely fine. I'm complaining about situations where you end up passing names of functions around (as strings), and they take on special meanings along the way. I characterise this as spaghetti code because it leads to a situation whereby control flow is embedded both within constructs in the language, and within computed values based on using indirection on strings-indexing-functions. That's just nasty.
Just so I'm not misinterpreted, let me furnish my complaint with an example to show what I am and am not complaining about:
...
$x = "hello$z$k$y";
$x($foobar);
vs.
...
$x = function() { echo "foo"; };
$x();
The latter is (mostly) fine. The former is completely bonkers, and is what I am complaining about - the ability to do ad hoc indirection based on calculated values. In the first case, functions are not values, and strings have become semantically significant. In the latter case, the function is a first-class value, and there is absolutely no magic going on - everything is hunky dory.
> Most uses for dynamic invocation are at the utility/framework level, not in application land.
I still can only think of a few restricted cases where indirection based upon strings (that values of which are not known until runtime) would be sensible or useful. The kinds of cases I can think of are where you would want to do something really dubious based on user-input. Keep in mind that I'm not railing against reflection here! Any sensible dynamic language should be able to provide you with the means to get a function value based on a name. This is not what you are describing. I think name-based reflection could have a use in utility or framework code, but relying on this kind of unrestricted indirection is really just a sign of deeper problems, I think.
> You're right, it's dangerous when used improperly
Which uh... was my point.
> but you should consider coming down from the theory clouds if you believe dynamic invocation and goto are in the same boat on the river styx.
Once again, you appear to be rebutting an argument I never made. Suggesting that I'm some sort of aloof theoretician is really a bit presumptuous. I developed an interest in all of those things you describe precisely because I am a programmer. Sure, I know a reasonable amount about theory, but I don't see that that should be used to discount any and all knowledge I have about practical programming too.
Let me make very clear my objection, once again. I am going to leave out exactly one definition from each of the following code snippets:
foobar(0);
vs.
$x(0);
I left out the definition of 'foobar' in the first snippet, and the definition of '$x' in the second. Can you tell me where program control goes in each case?
In the first case, you know that it goes to a function named "foobar" somewhere within scope.
In the second case, of course you can't! You have to go figure out how $x has been computed. You basically have to read most of the code in order to find out what value $x will have just to be able to predict what the program will do next! This is basically the same problem that goto-style code creates - it makes it considerably more difficult to mentally simulate the execution of the program.
Edit: This is getting down-voted? Really? I would love to know why.
Didn't intend to cause harm in "straw-man"ing. I think it's valuable to know where people are coming from. You care enough about programming languages and verification to have a blog on it, that's awesome. It's perspective.
No where in the post is there an example advocating dynamic invocation based on computed strings. No where. In fact, the more I read your responses the less I believe what you're saying has anything to do with the contents and examples of the post. We're on the same page here: dynamic invocation with computed strings: bad. spaghetti code. evil like eval.
> What does "dynamic invocation" have to do with higher-order functions?
Everything? Whether you're rolling your own or making use of PHP's built-in higher order functions, like array_map http://us.php.net/array_map, you're dynamically invoking something: a lambda, a function, an instance method, or a static method. Unfortunately these are distinct things in PHP. So with array_map its first argument is a callback, or something that returns true to is_callable as described in the post. What you're arguing is that you should never send a string name of a function to array_map, but should instead wrap the invocation in a lambda. So..
Point blank, it's just not in PHP. It's more than twice as slow, it takes more than twice the memory, and, most importantly is every bit as unsafe. Notice I misspelled lower with an extra w? Let's imagine that's a bug, a programmer's typo. Neither case will error out until array_map is executed which means both parse and generate opcode just fine. Reconsider the function call wrapped with in a lambda. What must it mean deep down for PHP to be able to generate valid opcodes on a pure method call to a method that doesn't exist? It means the internal opcode must simply store the method name as a string waiting to be executed. So once we hit the op a table lookup based on a string in your code happens. Both ways. One string is wrapped in quotes, the other in a far more elaborate construct.
As for reflection, I'm all for it but in PHP a reflected function or method object is not a callable, thus cannot be passed to a function like array_map. Even if you could, I do not believe that, in PHP, the following is any less evil:
Again, more memory, more code to execute, won't error out until runtime.
I really don't think you or I disagree on anything in terms of "best programming practices". The only thing we seem to disagree on is what this post was about and trying to demonstrate/advocate.
> No where in the post is there an example advocating dynamic invocation based on computed strings. No where.
Your example that uses a random number generator to select a string and then use that string as a function name is dynamic invocation based on computed strings.
> What you're arguing is that you should never send a string name of a function to array_map, but should instead wrap the invocation in a lambda.
No, that's not what I'm arguing at all. Your example above uses a string literal, which is basically the same as having a first-class identifier in its place. I don't think that kind of behaviour significantly harms readability. However, there's really nothing more "dynamic" about this kind of invocation than any other invocation in PHP.
I kinda question your use of "dynamic invocation" as a blanket term to cover first-class anonymous function application, higher-order functions and indirection. These are all separate-but-related concepts. My cursory Googling didn't turn up any uses of 'dynamic invocation' that are anything like the way you are using it - do you have a source or reference or is this your terminology?
> What must it mean deep down for PHP to be able to generate valid opcodes on a pure method call to a method that doesn't exist? It means the internal opcode must simply store the method name as a string waiting to be executed
Perhaps I'm misunderstanding what the internals of the PHP interpreter are these days, but you appear to be describing the use of a symbol table or something. The use of 'opcode' confuses me, because if there was code generation going on, it would be possible to statically resolve names to first-class function values.
What I'm getting at is that I don't see that there is a difference (in PHP) between the "dynamic invocation" you are describing, and straightforward run-of-the-mill function-calling, given that PHP's internal representation of both is essentially just a string (which is looked up in some symbol table in order to resolve it to a piece of code).
And so my point is that there is no real reason to exploit this quirk of language design in order to exploit indirection on computed strings (which, as I said in my previous post, I consider to be a kind of goto-esque spaghetti code feature).
I guess if you exclusively work on legacy codebases whicih already smell a certain way introducing stuff like this may cause some headache but if you have the opportunity to design something from the ground-up why would you NOT go for elegance?
I have no idea what about this you think would be 'elegant'. It's elegant in the same way that using computed offsets into raw memory is elegant in C, which is to say that it's inherently fragile, prone to very subtle bugs, and detrimental to readability.
And given that it's in PHP, it doesn't even really have the absurd machismo of doing dubious things with pointer arithmetic in C going for it.
"is_callable" is no better, because if you don't know, you shouldn't even be trying it. It might as well be called "does_stick", because you're basically just throwing spaghetti (code) at a wall.