Generating Power with Yield

43
Generating Power with Yield / Jason Myers @jasonamyers

description

My talk for the Nashville PHP Users Group August 2013 meeting.

Transcript of Generating Power with Yield

Page 1: Generating Power with Yield

GeneratingPower with Yield

/ Jason Myers @jasonamyers

Page 2: Generating Power with Yield

Yield, a modern language lovestory

Originally Proposed in 1995, the yield keyword became official via the RFP on June 20th 2013 with PHP 5.5.Generators

Facebook said a Hip, Hop and Don't Stop, and did their own yieldgenerators in HipHop PHP

Page 3: Generating Power with Yield

They are HEAVILY BASED off of Python, with a nod towards theMozilla JS implementation and the await C# concept.

Page 4: Generating Power with Yield
Page 5: Generating Power with Yield
Page 6: Generating Power with Yield

IteratorAn object that lets us traverse a container

Page 7: Generating Power with Yield
Page 8: Generating Power with Yield

PHP Iterator Interface Iterator extends Traversable { /* Methods */ abstract public mixed current ( void ) abstract public scalar key ( void ) abstract public void next ( void ) abstract public void rewind ( void ) abstract public boolean valid ( void ) }

For example checkout classArrayIterator

Page 9: Generating Power with Yield

ArrayIterator Example $fruits = array( "apple" => "yummy", "orange" => "ah ya, nice", "grape" => "wow, I love it!", "plum" => "nah, not me" ); $obj = new ArrayObject( $fruits ); $it = $obj->getIterator(); echo "Iterating over: " . $obj->count() . " values\n"; while( $it->valid() ) { echo $it->key() . "=" . $it->current() . "\n"; $it->next(); }

Iterating over: 4 values apple=yummy orange=ah ya, nice grape=wow, I love it! plum=nah, not me

Page 10: Generating Power with Yield

Generatora special routine that can be used to control the iteration behavior of

a loop, and yields the values one at a time

Page 11: Generating Power with Yield

TL;DRA generator looks like a function but behaves like an iterator

Page 12: Generating Power with Yield
Page 13: Generating Power with Yield

Performant? range(0, 1000000)

Uses over 100MB of RAM

Page 14: Generating Power with Yield
Page 15: Generating Power with Yield

Generator Version function xrange($start, $limit, $step = 1) { if ($start < $limit) { if ($step <= 0) { throw new LogicException('Step must be +ve'); } for ($i = $start; $i <= $limit; $i += $step) { yield $i; } } else { if ($step >= 0) { throw new LogicException('Step must be -ve'); } for ($i = $start; $i >= $limit; $i += $step) { yield $i; } } }

uses less than 1KB!

Page 16: Generating Power with Yield

TL; DR function xrange($min, $max) { for ($i = $min; $i < $max; $i++) { yield $i; } }

Page 17: Generating Power with Yield

Sequences function collatz($val) { yield $val;

while ($val != 1) { if ($val%2 == 0) { $val /= 2; } else { $val = 3*$val + 1; }

yield $val; } } foreach (collatz(11) as $c) { echo $c," "; }

11 34 17 52 26 13 40 20 10 5 16 8 4 2 1

Page 18: Generating Power with Yield
Page 19: Generating Power with Yield

Y U Only LOOPin?It will work for any function that takes an Iterator or a Traversable as

argument

$arr = iterator_to_array(collatz(11));

Page 20: Generating Power with Yield

Transformations function multiply_sequence($a, $fac) { foreach ($a as $val) { yield $val*$fac; } }

function to_html_list($input) { foreach ($input as $val) { yield "<li>".$val."</li>"; } }

Page 21: Generating Power with Yield

Chaining foreach (to_html_list(multiply_sequence(collatz(5),2)) as $val) { echo $val,"\n"; }

<li>10</li> <li>32</li> <li>16</li> <li>8</li> <li>4</li> <li>2</li>

Page 22: Generating Power with Yield

Selections function select_pattern($input, $pattern) { foreach ($input as $val) { if (preg_match($pattern, $val)) { yield $val; } } }

Page 23: Generating Power with Yield

Breath In function getLines($file) { $f = fopen($file, 'r'); if (!$f) { throw new Exception(); } while ($line = fgets($f)) { yield $line; } fclose($f); }

foreach (getLines("someFile") as $line) { doSomethingWithLine($line); }

Page 24: Generating Power with Yield

Breath Out function createLog($file) { $f = fopen($file, 'a'); while (true) { $line = yield; fwrite($f, $line); } } $log = createLog($file); $log->send("First"); $log->send("Second"); $log->send("Third");

Page 25: Generating Power with Yield
Page 26: Generating Power with Yield

Bro Remote Me!Fake the simultaneous processing of data

Page 27: Generating Power with Yield
Page 28: Generating Power with Yield

Green Threadsthreads that are scheduled by a virtual machine (VM/interperter?)

instead of natively by the underlying operating system

Page 29: Generating Power with Yield

function step1() { $f = fopen("file.txt", 'r'); while ($line = fgets($f)) { processLine($line); yield true; } }

Page 30: Generating Power with Yield

function step2() { $f = fopen("file2.txt", 'r'); while ($line = fgets($f)) { processLine($line); yield true; } }

Page 31: Generating Power with Yield

function step3() { $f = fsockopen("www.example.com", 80); stream_set_blocking($f, false); $headers = "GET / HTTP/1.1\r\n"; $headers .= "Host: www.example.com\r\n"; $headers .= "Connection: Close\r\n\r\n"; fwrite($f, $headers); $body = ''; while (!feof($f)) { $body .= fread($f, 8192); yield true; } processBody($body); }

Page 32: Generating Power with Yield

function runner(array $steps) { while (true) { foreach ($steps as $key => $step) { $step->next(); if (!$step->valid()) { unset($steps[$key]); } } if (empty($steps)) return; } } runner(array(step1(), step2(), step3()));

Page 33: Generating Power with Yield

ZOMG... THERE BE DRAGONS!This relies on making sure we have no blocking IO

Page 34: Generating Power with Yield

overREACTPHP much?event based, non-blocking IO - ReActPHP

Page 35: Generating Power with Yield

One More ThingSo if I can flip control, I can haz an Async?

Page 36: Generating Power with Yield

class Buffer { protected $reads, $data;

public function __construct() { $this->reads = new SplQueue(); $this->data = new SplQueue(); }

public function read() { if( $this->data->isEmpty() ) { $deferred = new \React\Promise\Deferred(); $this->reads->enqueue($deferred->resolver()); return $deferred->promise(); } else { return \React\Promise\When::resolve($this->data->dequeue()); } }

public function write($str) { if( $this->reads->isEmpty() ) { $this->data->enqueue($str); } else { $this->reads->dequeue()->resolve($str); } } }

Page 37: Generating Power with Yield

function printer(Buffer $buffer) { while( true ) { $value = ( yield Util::async($buffer->read()) );

echo "Printer: ", $value, PHP_EOL;

yield Util::async(nested_printer($buffer)); } }

Page 38: Generating Power with Yield

function nested_printer(Buffer $buffer) { for( $i = 0; $i < 5; $i++ ) { // Yield a promise task and wait for the result - this is non-blocking $value = ( yield Util::async($buffer->read()) );

echo "Nested printer: ", $value, PHP_EOL; } }

Page 39: Generating Power with Yield

$buffer = new Buffer();

$scheduler = new \Async\Scheduler();

$scheduler->add(new \Async\Task\GeneratorTask(printer($buffer)));

$i = 0; $scheduler->add(new \Async\Task\RecurringTask( function() use($buffer, &$i) { $buffer->write(++$i); } ));

$scheduler->run();

Printer: 1 Nested printer: 2 Nested printer: 3 Nested printer: 4 Nested printer: 5 Nested printer: 6 Printer: 7 Nested printer: 8 Nested printer: 9 Nested printer: 10 Nested printer: 11 Nested printer: 12 ...

Page 40: Generating Power with Yield

$loop = \React\EventLoop\Factory::create();

$scheduler = new \Async\Scheduler();

$scheduler->add(new \Async\Task\RecurringTask([$loop, 'tick']));

$scheduler->run();

Page 41: Generating Power with Yield

AsyncCreated by Matt Pryor, on Bitbucket

Page 42: Generating Power with Yield

ThanksHuge thanks to Paul M. Jones and William Golden!

Page 43: Generating Power with Yield

THE END@jasonamyers