5 ms·
This feature seems seriously contrived and they could have tried to learn a thing or two from ruby here. The idea that the code flow is now no longer linear, a
by SeoxyS 14y ago
This feature seems seriously contrived and they could have tried to learn a thing or two from ruby here.
The idea that the code flow is now no longer linear, and that the programmer now has to understand perfectly the odd rules by which the execution of code now just jumps around… boggles the mind.
A closure-based approach would have been much more semantically sane; if a little syntactically ugly. But then again, how hard would it have been to add a nicer closure syntax to PHP, instead of creating this mess of a feature?
Take this:
def each_line_in_file
f = File.open('hello.txt', 'r')
while line = f.gets
yield line
end
f.close
end
each_line_in_file do |l|
puts l
end
This is a real-world Ruby example of the feature. The semantics here are clear, each_line is a higher-order function which takes a closure as an arguments, and calls it (yields to it) for each line in the file. Ruby allows the programmer to use the yield keyword to avoid having to declare the closure as an argument (`def each_line(&block)`).
The same can even be implemented in PHP as it stands today, although the syntax is quite unsightly:
function each_line_in_file($callback) {
$f = fopen('hello.txt', 'r');
while (null !== ($line = fgets($f))) {
$callback($line);
}
fclose(f);
}
each_line_in_file(function ($l) {
echo $l."\n";
});
- masklinn 14y ago> 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?
- SeoxyS 14y ago> 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…
- jasonlotito 14y agoNot familiar enough with Ruby, but wouldn't your Ruby sample have this same problem?
- anonymoushn 14y agoNo, the ruby "generator" is actually a function that takes a function and calls it with each line from the file, like this: >>> the_list = [1,2,4,7] >>> def ruby_generator(f): ... for x in the_list: ... f(x) ... >>> def print_it(x): ... print x ... >>> ruby_generator(print_it) 1 2 4 7
- jasonlotito 14y ago
- TazeTSchnitzel 14y ago>The idea that the code flow is now no longer linear, and that the programmer now has to understand perfectly the odd rules by which the execution of code now just jumps around… boggles the mind. They aren't odd rules. It's just a coroutine. It executes, yields a value to the calling function and yields back control, the calling function does stuff, and yields back control, and this repeats. It's quite simple. Also, don't argue against it here. Argue with the Python and C# folks, they did it first.
- nikic 14y agoI'm not really sure what I should with this comment. It doesn't make sense to me. Generators and coroutines are a well-known concept implemented in many languages, in particular Python and C#. It is also part of ECMAScript Harmony as far as I know. Other languages implement the more generalized concept of continuation passing (usually providing generators implemented on top of them). Callbacks can be used to implement some of the use cases of generators. To be more precise they can be used if the callback is stateless (as in your above case). If the callback had to handle state, you would have manually implement a state machine, making the code hard to understand and adding a lot of boilerplate. Generally PHP does not make extensive use of callback-y programming, but does make extensive use of iteration. That's why generators integrate much better than callback based producers. The same applies to Python, that's why they too have generators. You need to understand that Ruby has a different underlying philosophy, which is not directly transferred to other languages. What might be nice in Ruby will be considered really ugly in Python or PHP and vice-versa.
- jablan 14y agoNot sure if you are familiar with Enumerator class in Ruby, it does pretty much what generators in PHP are about: require 'enumerator' def each_line_from_file file_name Enumerator.new do |yielder| File.open(file_name) do |file| while line = file.gets yielder << line ## or, alternatively: # yielder.yield(line) end end end end each_line_from_file('test.txt').each do |l| puts l end