Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:88053 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 43670 invoked from network); 4 Sep 2015 17:34:08 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 4 Sep 2015 17:34:08 -0000 Authentication-Results: pb1.pair.com smtp.mail=morrison.levi@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=morrison.levi@gmail.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.213.51 as permitted sender) X-PHP-List-Original-Sender: morrison.levi@gmail.com X-Host-Fingerprint: 209.85.213.51 mail-vk0-f51.google.com Received: from [209.85.213.51] ([209.85.213.51:34527] helo=mail-vk0-f51.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 1D/16-33222-F06D9E55 for ; Fri, 04 Sep 2015 13:34:08 -0400 Received: by vkhf67 with SMTP id f67so15443126vkh.1 for ; Fri, 04 Sep 2015 10:34:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:sender:in-reply-to:references:date:message-id:subject :from:to:cc:content-type; bh=9hcLBIEJM51+qLSqDW8lv3I7HL36cW84/wipo5tAxAU=; b=IklJnd9sNXykp0WwDiYvMoupx3wOz81eZ1XQJACh45F+LmzmBBzOP6+pXtpN49KXeR qybF06miKrAdGcCTvVYVpQjMKulDI607V9aZFJGWE7RJF8cONB6YTwWyg2oO9UmMy3TF Ltf9oYxLzPVLYDsMU6wunei/OoZcXqTvUJPMfUTF/ad6rAFFaW9DE8NjLNDCN5hSHi3B ZPY4TvmxcnAxwww7BJPlxvZugzQUY1eEwnXjcyVeExKJpr9Wd1Ej95RIAZOXjpvJ2wqR h/05mcEM6yCgQAdO6sKQ8ILI8w3MPUsic4ZxiPIhZV9lPy4NNsHTP9qIzgZzAiSf5Fza XwHw== MIME-Version: 1.0 X-Received: by 10.52.106.231 with SMTP id gx7mr5856507vdb.55.1441388044600; Fri, 04 Sep 2015 10:34:04 -0700 (PDT) Sender: morrison.levi@gmail.com Received: by 10.31.16.68 with HTTP; Fri, 4 Sep 2015 10:34:04 -0700 (PDT) In-Reply-To: References: <55E77CA9.7050609@gmail.com> <55E81959.6040305@gmail.com> Date: Fri, 4 Sep 2015 11:34:04 -0600 X-Google-Sender-Auth: s6c5EjTf0lbQv6rKGoTNFKLLBDo Message-ID: To: Daniel Persson Cc: =?UTF-8?Q?Pavel_Kou=C5=99il?= , PHP Internals Content-Type: text/plain; charset=UTF-8 Subject: Re: [PHP-DEV] [RFC] [Discussion] Short Closures From: levim@php.net (Levi Morrison) (This post is fairly lengthy so I have copied it into a gist if you would prefer to read it that way: https://gist.github.com/morrisonlevi/8323125182ca3b5e2124) Here's a practical example of chaining closures together that allows us to easily implement versions of `array_map` and `array_reduce` that are highly generic, meaning they can be used with various kinds of input and output types instead of just arrays. These new versions can also easily be chained together to form a "pipe" of transformations. While the short-form closures are not required for this to work it does make it much simpler. Also, I know this is a fair bit of code to dump at once; it's really hard to give meaningful examples that don't have some length to them. Please don't skip this code! $map = $fn ~> $input ~> { foreach ($input as $key => $value) { yield $key => $fn($value); } }; $reduce = $initial ~> $fn ~> $input ~> { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; }; // this is just a helper function // can skip reading it if you want function chain(callable $fn, callable ...$callables) { $functions = func_get_args(); return (...$params) ~> { $count = count($functions); $carry = $functions[0](...$params); for ($i = 1; $i < $count; $i++) { $carry = $functions[$i]($carry); } return $carry; }; } This essentially allows for binding only certain parameters and chaining whole algorithms together: $algorithm = chain( $map($x ~> $x * 2), $reduce([])(function ($accumulator, $value) { $accumulator[] = $value; return $accumulator; }) ); $result = $algorithm($iterable); Note that while I've used a basic array here for brevity I could have used any kind of container that supports adding single items. For instance, I could have used a set: $set = new Set(); $algorithm = chain( $map($x ~> $x * 2), $reduce($set)(function ($set, $value) { $set->add($value) return $set; }) ); $result = $algorithm($iterable); Now all the duplicates get removed should there be any. ----- Hopefully I've been able to demonstrate that this style of coding is powerful and that the chaining of closures was helpful. Here are the long-form closure equivalents for contrast: $map = function ($fn) { return function($input) use($fn) { foreach ($input as $key => $value) { yield $key => $fn($value); } }; }; $reduce = function ($initial) { return function ($fn) use($initial) { return function($input) use($initial, $fn) { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; }; }; }; function chain(callable $fn, callable ...$callables) { $functions = func_get_args(); return function(...$params) use($functions) { $count = count($functions); $carry = $functions[0](...$params); for ($i = 1; $i < $count; $i++) { $carry = $functions[$i]($carry); } return $carry; }; } $algorithm = chain( $map(function ($x) { return $x * 2; }), $reduce([])(function ($accumulator, $value) { $accumulator[] = $value; return $accumulator; }) ); ---- Some people have also suggested removing the block syntax for short closures. The implementation of reduce as defined above is a demonstration of why the block syntax is helpful: $reduce = $initial ~> $fn ~> $input ~> { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; }; With the block syntax removed the last closure in the chain has to use long-form like this: $reduce = $initial ~> $fn ~> function($input) use($initial, $fn) { $accumulator = $initial; foreach ($input as $value) { $accumulator = $fn($accumulator, $value); } return $accumulator; }; I hope you'll agree with me that this is just weird. I was initially against the block syntax but after using it in meaningful code I concluded that the block syntax is helpful.