Re: [Discussion] Callable types via Interfaces

From: Date: Thu, 20 Apr 2023 23:23:13 +0000
Subject: Re: [Discussion] Callable types via Interfaces
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-120093@lists.php.net to get a copy of this message
On Thu, Apr 20, 2023 at 11:25 AM Larry Garfield <larry@garfieldtech..com> wrote: > > Hi folks. This is a pre-discussion, in a sense, before a formal RFC. Nicolas Grekas and I > have been kicking around some ideas for how to address the desire for typed callables, and have > several overlapping concepts to consider. Before going down the rabbit hole on any of them we want > to gauge the general feeling about the approaches to see what is worth pursuing. > > We have three "brain dump" RFCs on this topic, although these are all still in > super-duper early stages so don't sweat the details in them at this point. We just want to > discuss the basic concepts, which I have laid out below. > > > https://wiki.php.net/rfc/allow_casting_closures_into_single-method_interface_implementations > > https://wiki.php.net/rfc/allow-closures-to-declare-interfaces-they-implement > https://wiki.php.net/rfc/structural-typing-for-closures > > ## The problem > > function takeTwo(callable $c): int > { > return $c(1, 2); > } > > Right now, we have no way to statically enforce that $c is a callable that takes 2 ints and > returns an int. We can document it, but that's it. > > There is one loophole, in that an interface may require an __invoke() method: > > interface TwoInts > { > public function __invoke(int $a, int $b): int; > } > > And then a class may implement TwoInts, and takeTwo() can type against TwoInts. However, that > works only for classes, which are naturally considerably more verbose than a simple closure and > represent only a subset of the possible callable types. > > The usual discussion has involved a way to specify a callable type's signature, like so: > > function takeTwo(callable(int $a, int $b): int $c) > { > return $c(1, 2); > } > > But that runs quickly into the problem of verbosity, reusability, and type aliases, and the > discussion usually dies there. > I'm going to stop here. Two big things: 1. I think reducing verbosity can be left for the future. We don't have to solve that right now. 2. I think there's another more important reason previous attempts failed: they will inevitably burden the programmer without type inference.For the moment, let's assume this signature: function takeTwo(callable(int $x, int $y): int $c); What happens if I pass a short-closure? takeTwo(fn ($x, $y) => $x + $y); I would be annoyed if I had to write the type info, but particularly the return type. Today, if I just used a static analysis tool, there's no problem: /** @param callable(int $x, int $y): int $c */ function takeTwo(callable $c); takeTwo(fn ($x, $y) => $x + $y); 3. And another reason they failed: callables are going to want generic types pretty commonly. Think array_filter, array_map, etc. So, I think these brain dump RFCs are all focusing on the wrong problems.

« previous php.internals (#120093) next »