Top Banner
Generating Power with Yield / Jason Myers @jasonamyers
43

Generating Power with Yield

Jan 15, 2015

Download

Technology

Jason Myers

My talk for the Nashville PHP Users Group August 2013 meeting.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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