Re: [RFC] Callable Types

From: Date: Tue, 26 Apr 2016 18:38:33 +0000
Subject: Re: [RFC] Callable Types
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-92813@lists.php.net to get a copy of this message
Bringing this thread to the main one On Sun, 24 Apr 2016 06:27:39 +0300, Mathieu Rochette <mathieu@rochette.cc> wrote:
I've seen someone mentioning variadics so I made a few small tests, I think those two should work (they currently don't): https://3v4l.org/eZgR9/rfc#rfc-callable_typehint https://3v4l.org/N7i0u/rfc#rfc-callable_typehint and this one should not: https://3v4l.org/fcRlT/rfc#rfc-callable_typehint
Hey, You're right, this code should be valid
    function foo(callable() $a) { $a(); }
    foo(function (...$args) {});
But it gets into the same problems like optional params when it is typed
    function foo(callable() $a) { $a(123); }
    foo(function (A ...$args) {});
Now to the question why would foo() call $a with additional arguments even though it assumes $a is of type callable()? Well at first because it can - this is enough for it to be not type safe :P Long answer is: in the real life there will definitely be cases where this would allow users to shoot their feet, take this example:
    function map(/* array | Traversable */ $collection, callable($value, $index) $mapper) {
        $out = [];
        foreach ($collection as $index => $value) {
            $out[] = $mapper($value, $index);
        }
        return $out;
    }
...
    function renderPost(Post $p, bool $asAdmin = false) {
        return render("post_template", ["post" => $p, "asAdmin" => $asAdmin]);
    }
...
    $renderedPosts = map($posts, "renderPost");
    // here developer would get fatal at the line `$out[] = $mapper($value, $index);`
    // if optional parameters were supported
    // Without them though, he gets TypeError when he passes "renderPost" to map()
    // And it says nicely what exactly he has done wrong
Even now though, he could get into this situation if $asAdmin parameter was not properly typed, but that would be on him as he'd need to voluntarily drop those types for which I don't see a reason to do so. Now talking about an immediate solution to hypothetical problem we are facing there is an easy solution - if you have a functional library, you'll probably have there partial_any function of some kind (ex. [0]), so what you could instead do is:
    $renderedPosts = map($posts, f\partial_any("renderPost", f\placeholder(), true));
This would always pass true to a second parameter, but leave the first one. [0] https://github.com/lstrojny/functional-php/blob/master/docs/02-partial-application.md#partial_any

« previous php.internals (#92813) next »