> This feature seems seriously contrived and they could have tried to learn a thing or two from ruby here.
That's way too late, PHP is an imperatively styled and statements-based language, it uses iterables and external iteration, not internal. That's what the language is.
> The semantics here are clear
There's nothing unclear about generator semantics, regardless of your refusal to understand them.
> The same can even be implemented in PHP as it stands today
Not quite, PHP doesn't have non-local returns, so you soon get into annoying issues of not being able to break away from an iteration (or of having an utterly terrible API to do so, as in [NSArray enumerateObjectsUsingBlock:]
> Ruby allows the programmer to use the yield keyword to avoid having to declare the closure as an argument (`def each_line(&block)`).
So you dislike one magic but you like an other for pretty much no reason at all beyond knowing one and not the other? Consistency not your style?
> So you dislike one magic but you like an other for pretty much no reason at all beyond knowing one and not the other? Consistency not your style?
The thing I love about Ruby's magic is that it's not really magic, it's an abstraction of solid fundamentals.
There's nothing really magic about ruby's yield, because it's a shortcut for explicitly declaring a closure and calling it. These are real language concepts that are well understood. PHP's yield, however, is a completely new construct. The jumping around does not follow well-understood rules (like calling a function). In ruby's case, that's what it does. It simply executes the function, and calls a closure.
In PHP now when you define a function, and then call that function; it may or may not execute the code inside that function, depending on whether later on in the function there's a `yield` keyword. That's insane!
Say you can see this in your code editor:
public function hello() {
// a lot of important code
$myvar = $this->other_cool_function();
// more code
}
public function other_cool_function() {
echo "Hello, world!\n";
. . .
You're looking at line 3, the line which calls the other cool function. Looking at this code you'd expect that to print "Hello, world!" first, and do some other stuff in the rest of the function. But wait! You didn't notice, 30 lines down the other function, there's a yield keyword, which means that the entire function doesn't ever execute until you start iterating over it…
> In PHP now when you define a function, and then call that function; it may or may not execute the code inside that function, depending on whether later on in the function there's a `yield` keyword. That's insane!
It's exactly how it works in C# and Python. When you call a function, you don't care how it does what it does -- only that it returns a result. The syntax and design works this way in all sorts of languages because it's easier to reason about. I've only used generators in C# but it's amazingly simple to use. The point of an abstraction is that it hides the details. You're overly concerned with the details that are neatly and logically hidden away.
This isn't really PHP's fault. These generators and the generators that are creeping into Javascript are based on Python's, which have the same issue you mention:
Of course, I would prefer to have coroutines or call/cc, but generally these get shot down as terrible ideas by the people who are actually in charge of making decisions. In Python you can get around this using greenlet. I'm told HipHop has these goodies as well. No dice if you need to write Javacript or non-Facebook PHP, though.
So how function executes depends on the content of the function? And return value depends on what's inside the function? Insane! Next thing they'd say you actually have to document your functions and describe how it works and what it does! That you have to comment code! Adness, utter adness! Nobody would use a language that doesn't have every function's purpose obvious from the name!
Wait, what? Numerous languages implement it in exactly the same way? Are they called "Ruby"? No? Then who cares, it's not Ruby so it must be wrong. Not just wrong, it's insane - nobody sane would implement a language that doesn't work exactly like Ruby!
You could write code like that in PHP, and the GP post with the ruby example does so, but PHP generators do not work like that.
> I think you might be confused. I'm referring to the GP post that has the example with the yield call in it, the ruby example.
The Ruby example doesn't have the problem you mentioned because Ruby doesn't have generators at all. In Python and PHP, yield means "Suspend execution of this function and return a value to the caller, who can later call this function again to resume its execution." In Ruby, yield means "Call the block that was passed to this function with the values after yield," or exactly what I've written in Python without using the yield keyword. Ruby's "yeild line" is Python's "f(x)".
Edited: I think I understand what you mean after reading once more. If you mistook the function containing yield in Ruby for a function that does not contain yield you would still have a problem, but not the same problem you would have in PHP or Python. It would raise an exception, while PHP and Python's would just return an iterable.
def each_line_in_file
.. yield 1
.. end
=> nil
each_line_in_file
(eval):2: (eval):2:in `each_line_in_file': no block given (LocalJumpError)
from (eval):3
That's way too late, PHP is an imperatively styled and statements-based language, it uses iterables and external iteration, not internal. That's what the language is.
> The semantics here are clear
There's nothing unclear about generator semantics, regardless of your refusal to understand them.
> The same can even be implemented in PHP as it stands today
Not quite, PHP doesn't have non-local returns, so you soon get into annoying issues of not being able to break away from an iteration (or of having an utterly terrible API to do so, as in [NSArray enumerateObjectsUsingBlock:]
> Ruby allows the programmer to use the yield keyword to avoid having to declare the closure as an argument (`def each_line(&block)`).
So you dislike one magic but you like an other for pretty much no reason at all beyond knowing one and not the other? Consistency not your style?