FAQ
this topic may be discussed before, but i'd bring it up again.

closure was introduced in PHP 5.3, function with its context can now
be stored in an callable object(or a handler in string type, whatever)
i think it's easy and read to introduce generator and yield operator
in php

let's see what was in Iterator:
1. every class/interface can be prototyped in php userland, not
internal, although many of the them was builtin written in c so make
life easier
pro: "simple" to implement in ZendEngine, see note 2 aboive
con: really ugly to code Iterator/Filter in userland, hard to
understand how they worked, code has to be split in rewind/next
functions
2. no changes is required in ZendEngine, except powering up "foreach"
to support iterator, so ppl can iterate over it much more easier.
pro: "simple" to implement in ZendEngine, see note 2 aboive
con: -

let's see what will yeild/generator looks like:
1. require saving function calling context (not call stack)
con(was): there was no way to save it, we had to implement it first
con(now): no con, implemented already in 5.3
2. just 1 function, yield at any where in the body
pro: much more readable than Iterator
con: "yield" is now reserved keyword
example:
function foo($space_and_new_line = false)
{
yield "hello";
if ($space_and_new_line) {
yield " ";
}
yield "world";
if ($space_and_new_line) {
yield PHP_EOL;
}
}

$g = foo(true); // function foo() is not called here
foreach ($g as $i) { // function foo() IS called here
echo $i;
}

function filter($g, $regex)
{
foreach ($g as $baa) {
if (preg_match($regex, $i)) {
return true;
} // if
} // foreach
}

$g = foo(true);
foreach (filter($g, "^\\w+$") as $i) {
echo $i; // now letters only
}

$g = foo(true);
echo $g(); // same as: echo $g->value(); $g->next(); this is not in
python, neither in php iterator
echo $g();
echo $g();
(btw, i think iterator should be able to be used in this way)

I don't think you can reimplement it into Iterator in any simpler
form, except that you can reuse those classes already implemented.

is it perl way? no. because yield/generator make code much more
readable, easier to understood, not just "easier" to write, it's the
php way

==========
for those who don't know what yield/generator is, you can see python
document or the following brief (correct me if i'm wrong)
. functions (named or unamed/closure) with yield in its body, is a
generator function that with context saved, not a normal. it's similar
as closure context but with "yield point" saved
. when you call a generator function, it returns an generator object
. the interface to generator object is exactly like Iterator
interface, that can be design to have rewind/next/valid methods, etc
. generator is an OuterIterator
. you can call $generator->next() (and other methods) explicitly, or
call foreach which call iterator/generator methods implictly
. the only difference between iterator and generator is how they
implement it inside. when php execute "yield", the generator is
execution is returned but its execution context is saved, next time
the generator is iterated, php execute from the last yield point with
all context restored.

Search Discussions

  • Etienne Kneuss at Jan 13, 2009 at 2:29 pm
    Hello,
    On Tue, Jan 13, 2009 at 1:30 PM, moo.tinys wrote:
    this topic may be discussed before, but i'd bring it up again.

    closure was introduced in PHP 5.3, function with its context can now
    be stored in an callable object(or a handler in string type, whatever)
    i think it's easy and read to introduce generator and yield operator
    in php

    let's see what was in Iterator:
    1. every class/interface can be prototyped in php userland, not
    internal, although many of the them was builtin written in c so make
    life easier
    pro: "simple" to implement in ZendEngine, see note 2 aboive
    con: really ugly to code Iterator/Filter in userland, hard to
    understand how they worked, code has to be split in rewind/next
    functions
    2. no changes is required in ZendEngine, except powering up "foreach"
    to support iterator, so ppl can iterate over it much more easier.
    On the contrary, It will most likely require a lot of changes to
    provide structures that are able to store their execution context to
    be able to resume it later. I.e.

    function foo() {
    error_reporting(0);
    yield "foo";
    trigger_error("foo", E_NOTICE);
    yield "bar";
    }
    $e = false;
    error_reporting(E_ALL);
    foreach (foo() as $b) {
    if (!$e) { $e = true; error_reporting(E_ALL); }
    }

    1) will the error be thrown?
    2) what will the error_reporting value be after the execution of the function ?

    error_reporting is a setting that is shared, and here you'd like to
    resume the execution using the old context (error_reporting = 0) to
    continue the function, affecting the outer context, which will stick
    after the function ends? Or do you plan to keep both contexts
    completely separated and create inconsistencies?
    pro: "simple" to implement in ZendEngine, see note 2 aboive
    con: -

    let's see what will yeild/generator looks like:
    1. require saving function calling context (not call stack)
    con(was): there was no way to save it, we had to implement it first
    con(now): no con, implemented already in 5.3
    How is that implemented already in 5.3 ?
    2. just 1 function, yield at any where in the body
    pro: much more readable than Iterator
    con: "yield" is now reserved keyword
    example:
    function foo($space_and_new_line = false)
    {
    yield "hello";
    if ($space_and_new_line) {
    yield " ";
    }
    yield "world";
    if ($space_and_new_line) {
    yield PHP_EOL;
    }
    }

    $g = foo(true); // function foo() is not called here
    foreach ($g as $i) { // function foo() IS called here
    echo $i;
    }

    function filter($g, $regex)
    {
    foreach ($g as $baa) {
    if (preg_match($regex, $i)) {
    return true;
    } // if
    } // foreach
    }

    $g = foo(true);
    foreach (filter($g, "^\\w+$") as $i) {
    echo $i; // now letters only
    }
    That example looks wrong(and I hope it is):
    - $i is not defined in the scope of filter()
    - filter() returns true or null. You can't iterate on such values.

    Also, could you provide an example where this construct is actually useful ?

    The example you provided doesn't demonstrate anything, as it can be
    implemented in a far easier way using:

    function foo($s) {
    return $s ? array('hello', ' ', 'world', PHP_EOL) : array('hello', 'world');
    }

    foreach(foo() as $word) {
    ...
    }

    additionally, SPL's filterIterator and arrayiterator can be used to
    implement a filtered iteration on the array.
    $g = foo(true);
    echo $g(); // same as: echo $g->value(); $g->next(); this is not in
    python, neither in php iterator
    echo $g();
    echo $g();
    This syntax is definitely not self-explanatory, and will make things
    really hard to understand.
    (btw, i think iterator should be able to be used in this way)
    I don't think you can reimplement it into Iterator in any simpler
    form, except that you can reuse those classes already implemented.

    is it perl way? no. because yield/generator make code much more
    readable, easier to understood, not just "easier" to write, it's the
    php way

    ==========
    for those who don't know what yield/generator is, you can see python
    document or the following brief (correct me if i'm wrong)
    . functions (named or unamed/closure) with yield in its body, is a
    generator function that with context saved, not a normal. it's similar
    as closure context but with "yield point" saved
    . when you call a generator function, it returns an generator object
    . the interface to generator object is exactly like Iterator
    interface, that can be design to have rewind/next/valid methods, etc
    . generator is an OuterIterator
    . you can call $generator->next() (and other methods) explicitly, or
    call foreach which call iterator/generator methods implictly
    . the only difference between iterator and generator is how they
    implement it inside. when php execute "yield", the generator is
    execution is returned but its execution context is saved, next time
    the generator is iterated, php execute from the last yield point with
    all context restored.
    I'm skeptic at what value this construct would really add to PHP. And
    my guess is, it will not be worth the trouble implementing it.

    Regards,
    --
    PHP Internals - PHP Runtime Development Mailing List
    To unsubscribe, visit: http://www.php.net/unsub.php




    --
    Etienne Kneuss
    http://www.colder.ch

    Men never do evil so completely and cheerfully as
    when they do it from a religious conviction.
    -- Pascal
  • Moo.tinys at Jan 14, 2009 at 2:20 am

    On the contrary, It will most likely require a lot of changes to
    provide structures that are able to store their execution context to
    be able to resume it later. I.e.

    function foo() {
    error_reporting(0);
    yield "foo";
    trigger_error("foo", E_NOTICE);
    yield "bar";
    }
    $e = false;
    error_reporting(E_ALL);
    foreach (foo() as $b) {
    if (!$e) { $e = true; error_reporting(E_ALL); }
    }

    1) will the error be thrown?
    2) what will the error_reporting value be after the execution of the function ?

    error_reporting is a setting that is shared, and here you'd like to
    resume the execution using the old context (error_reporting = 0) to
    continue the function, affecting the outer context, which will stick
    after the function ends? Or do you plan to keep both contexts
    completely separated and create inconsistencies?
    pro: "simple" to implement in ZendEngine, see note 2 aboive
    con: -

    let's see what will yeild/generator looks like:
    1. require saving function calling context (not call stack)
    con(was): there was no way to save it, we had to implement it first
    con(now): no con, implemented already in 5.3
    How is that implemented already in 5.3 ?
    i haven't dig deep into how closure is implemented, but it's a big
    step forward, yield/generator function is another minor step base on
    or smilar as closure
    2. just 1 function, yield at any where in the body
    pro: much more readable than Iterator
    con: "yield" is now reserved keyword
    example:
    function foo($space_and_new_line = false)
    {
    yield "hello";
    if ($space_and_new_line) {
    yield " ";
    }
    yield "world";
    if ($space_and_new_line) {
    yield PHP_EOL;
    }
    }

    $g = foo(true); // function foo() is not called here
    foreach ($g as $i) { // function foo() IS called here
    echo $i;
    }

    function filter($g, $regex)
    {
    foreach ($g as $baa) {
    foreach ($g as $i) { // <- correction, and sorry, here's the right version
    if (preg_match($regex, $i)) {
    return true;
    yield $i; // <- correction
    } // if
    } // foreach
    }

    $g = foo(true);
    foreach (filter($g, "^\\w+$") as $i) {
    echo $i; // now letters only
    }
    That example looks wrong(and I hope it is):
    - $i is not defined in the scope of filter()
    - filter() returns true or null. You can't iterate on such values.

    Also, could you provide an example where this construct is actually useful ?
    every Iterator class can be rewritten into more simpler form in
    generator function
    every generator function can also be rewritten into more
    simpler/complexer form in Iterator class (most of the time, it's more
    complexer)

    for more complex code, i have to write it in pseudo code
    function fetch_items()
    {
    foreach (array(some builtin constant items here) as $item) {
    yield $item;
    }
    foreach (get_version1_urls() as $v1url) {
    $content = file_get_contents($v1url);
    foreach (parse_version1_content($content) as $item) {
    yield $item;
    }
    }
    foreach (get_version2_urls() as $v2url) {
    $content = file_get_contents($v2url);
    foreach (parse_version2_content($content) as $item) {
    yield $item;
    }
    }
    }

    yes, as i said you can rewrite it in iterator

    iterator class is a stateful iteratable implemented in class, while
    generator function is a stateful iteratable implemented in function
    (stateful version of function)
    if iterator class looks like a state machine that split code into
    multiple function/methods, generator function looks like thread base
    deisgn that can yield at some point which is sure more easier to write
    and understood
    you're skilled programmer like i am, understanding/writing iterator
    class is no big deal, but generator can make life much better for vast
    of php guys
    The example you provided doesn't demonstrate anything, as it can be
    implemented in a far easier way using:

    function foo($s) {
    return $s ? array('hello', ' ', 'world', PHP_EOL) : array('hello', 'world');
    }
    this is not in iterator class "framework"
    foreach(foo() as $word) {
    ...
    }

    additionally, SPL's filterIterator and arrayiterator can be used to
    implement a filtered iteration on the array.
    they're already implemented. yes, my example can be rewritten by
    reusing already implemented iterator class, but why not think about a
    case when ppl have to implemented their own iterator class? or have we
    all possible iterator class implemented already?
    $g = foo(true);
    echo $g(); // same as: echo $g->value(); $g->next(); this is not in
    python, neither in php iterator
    echo $g();
    echo $g();
    This syntax is definitely not self-explanatory, and will make things
    really hard to understand.
    well.. i'm not sure if this is useful either, just a thought when i
    bring this topic up :)
    I'm skeptic at what value this construct would really add to PHP. And
    my guess is, it will not be worth the trouble implementing it.
    thanks for your time

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupphp-internals @
categoriesphp
postedJan 13, '09 at 12:30p
activeJan 14, '09 at 2:20a
posts3
users2
websitephp.net

2 users in discussion

Moo.tinys: 2 posts Etienne Kneuss: 1 post

People

Translate

site design / logo © 2023 Grokbase