[RFC] [Vote] Callable types (now: Callable prototypes)

php.internals

Nikita

10 years ago
Evening internals, With this message I'd like to go to vote with the Callable prototypes RFC targeted at 7.1: https://wiki.php.net/rfc/callable-types We've renamed it (previously was "Callable types") as RFC names often dictate how users will call the feature and I want it to be more accurate/descriptive. Also the reflection part was added although I'm short on time currently, so implementation for that will be ready later. (speaking of implementation, it also currently doesn't use cache_slots - also something I'll add when I have a little bit of time)

Levi Morrison

10 years ago
On Mon, May 23, 2016 at 8:27 AM, Nikita Nefedov <inefedor@gmail.com> wrote:
> Evening internals, > > With this message I'd like to go to vote > with the Callable prototypes RFC targeted at 7.1: > https://wiki.php.net/rfc/callable-types > > We've renamed it (previously was "Callable types") as RFC names often > dictate how users will call the feature and I want it to be more > accurate/descriptive. > > Also the reflection part was added although I'm short on time currently, > so implementation for that will be ready later. (speaking of implementation, > it also currently doesn't use cache_slots - also something I'll add when > I have a little bit of time) > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php
A quick question before I vote: do callable prototypes allow for default parameters in the signature? There are examples for having a function passed that has a default parameters but I am not seeing it anywhere in the callable prototype definition so I thought I'd ask.

Nikita

10 years ago
> On 23 May 2016, at 19:31, Levi Morrison <levim@php.net> wrote: > >> On Mon, May 23, 2016 at 8:27 AM, Nikita Nefedov <inefedor@gmail.com> wrote: >> Evening internals, >> >> With this message I'd like to go to vote >> with the Callable prototypes RFC targeted at 7.1: >> https://wiki.php.net/rfc/callable-types >> >> We've renamed it (previously was "Callable types") as RFC names often >> dictate how users will call the feature and I want it to be more >> accurate/descriptive. >> >> Also the reflection part was added although I'm short on time currently, >> so implementation for that will be ready later. (speaking of implementation, >> it also currently doesn't use cache_slots - also something I'll add when >> I have a little bit of time) >> >> -- >> PHP Internals - PHP Runtime Development Mailing List >> To unsubscribe, visit: http://www.php.net/unsub.php > > A quick question before I vote: do callable prototypes allow for > default parameters in the signature? There are examples for having a > function passed that has a default parameters but I am not seeing it > anywhere in the callable prototype definition so I thought I'd ask.
Hey Levi, Although it could be done as a later RFC, my stance on it is that default values don't belong to the prototype of the function. Why I think that callable typehint doesn't need default values for params is: you either need this parameter to exist and even if there are cases where you'd like to pass some kind of 'default' value implicitly, you as a caller shouldn't impose on the callee responsibility to have this same value in his definition and instead just pass your argument explicitly... or if you don't need this parameter (as in you don't plan to pass anything explicitly) then you should just ask for a function without this parameter as there is clearly no need for it at all. Moreover I would argue interfaces have it wrong here too: the fact that default values are part of an interface yet their invariance is not enforced, yields an interesting sort of possible LSP violation. F.e. if we have an interface Logger (a completely unreal example but it shows what can go wrong) with function 'log($message, $level = LOG_DEBUG);' and a funny implementation that decided to change default value of $level to LOG_ALERT, then all callers of Logger#log who relied on the fact that level if not specified, will be LOG_DEBUG, who also happen to get the funny implementation of Logger will produce unneeded noise in the logs with their alert level messages which were intended to be debug messages :(

Levi Morrison

10 years ago
On Mon, May 23, 2016 at 1:19 PM, Nikita Nefedov <inefedor@gmail.com> wrote:
> >> On 23 May 2016, at 19:31, Levi Morrison <levim@php.net> wrote: >> >>> On Mon, May 23, 2016 at 8:27 AM, Nikita Nefedov <inefedor@gmail.com> wrote: >>> Evening internals, >>> >>> With this message I'd like to go to vote >>> with the Callable prototypes RFC targeted at 7.1: >>> https://wiki.php.net/rfc/callable-types >>> >>> We've renamed it (previously was "Callable types") as RFC names often >>> dictate how users will call the feature and I want it to be more >>> accurate/descriptive. >>> >>> Also the reflection part was added although I'm short on time currently, >>> so implementation for that will be ready later. (speaking of implementation, >>> it also currently doesn't use cache_slots - also something I'll add when >>> I have a little bit of time) >>> >>> -- >>> PHP Internals - PHP Runtime Development Mailing List >>> To unsubscribe, visit: http://www.php.net/unsub.php >> >> A quick question before I vote: do callable prototypes allow for >> default parameters in the signature? There are examples for having a >> function passed that has a default parameters but I am not seeing it >> anywhere in the callable prototype definition so I thought I'd ask. > > Hey Levi, > > Although it could be done as a later RFC, my stance on it is that default values don't belong to the prototype of the function. > > Why I think that callable typehint doesn't need default values for params is: you either need this parameter to exist and even if there are cases where you'd like to pass some kind of 'default' value implicitly, you as a caller shouldn't impose on the callee responsibility to have this same value in his definition and instead just pass your argument explicitly... or if you don't need this parameter (as in you don't plan to pass anything explicitly) then you should just ask for a function without this parameter as there is clearly no need for it at all. > > Moreover I would argue interfaces have it wrong here too: the fact that default values are part of an interface yet their invariance is not enforced, yields an interesting sort of possible LSP violation. F.e. if we have an interface Logger (a completely unreal example but it shows what can go wrong) with function 'log($message, $level = LOG_DEBUG);' and a funny implementation that decided to change default value of $level to LOG_ALERT, then all callers of Logger#log who relied on the fact that level if not specified, will be LOG_DEBUG, who also happen to get the funny implementation of Logger will produce unneeded noise in the logs with their alert level messages which were intended to be debug messages :(
Please document this restriction in the RFC. I guess I'm the only one who expected it to be there since it didn't come up before, but it just seems missing. You don't need to take it out of voting for this clarification, imo.

Nikita

10 years ago
On Mon, 23 May 2016 22:52:18 +0300, Levi Morrison <levim@php.net> wrote:
> On Mon, May 23, 2016 at 1:19 PM, Nikita Nefedov <inefedor@gmail.com> > wrote: >> >>> On 23 May 2016, at 19:31, Levi Morrison <levim@php.net> wrote: >>> >>> A quick question before I vote: do callable prototypes allow for >>> default parameters in the signature? There are examples for having a >>> function passed that has a default parameters but I am not seeing it >>> anywhere in the callable prototype definition so I thought I'd ask. >> >> Hey Levi, >> >> Although it could be done as a later RFC, my stance on it is that >> default values don't belong to the prototype of the function. >> >> Why I think that callable typehint doesn't need default values for >> params is: you either need this parameter to exist and even if there >> are cases where you'd like to pass some kind of 'default' value >> implicitly, you as a caller shouldn't impose on the callee >> responsibility to have this same value in his definition and instead >> just pass your argument explicitly... or if you don't need this >> parameter (as in you don't plan to pass anything explicitly) then you >> should just ask for a function without this parameter as there is >> clearly no need for it at all. >> >> Moreover I would argue interfaces have it wrong here too: the fact that >> default values are part of an interface yet their invariance is not >> enforced, yields an interesting sort of possible LSP violation. F.e. if >> we have an interface Logger (a completely unreal example but it shows >> what can go wrong) with function 'log($message, $level = LOG_DEBUG);' >> and a funny implementation that decided to change default value of >> $level to LOG_ALERT, then all callers of Logger#log who relied on the >> fact that level if not specified, will be LOG_DEBUG, who also happen to >> get the funny implementation of Logger will produce unneeded noise in >> the logs with their alert level messages which were intended to be >> debug messages :( > > Please document this restriction in the RFC. I guess I'm the only one > who expected it to be there since it didn't come up before, but it > just seems missing. You don't need to take it out of voting for this > clarification, imo.
Morning, sure thing, added it in the RFC

Bob Weinand

10 years ago
Hey, I have a question regarding strict/weak types. Currently, you cannot pass a callable function(string $foo) {} to a signature requiring (callable(int)), if I understood the code correctly. But weak types actually should allow that as it's totally fine to pass an integer to a string in weak mode. Is there any particular reason to this seemingly arbitrary restriction? Bob

Nikita

10 years ago
Hi Bob, When you pass an `int` to a `string` type parameter in weak mode it's being coerced to the needed type (not just directly passed). This is quite complex, because you'd need to copy zend_function and all its members (without changing the original zend_function). I would love to support it but all in all it comes down to implementation here for me, so I'd rather do it in a separate RFC. If you meant accepting `function(string $foo){}` as an argument of `callable(int)` parameter without coercing then this would be impossible due the fact that this all is context-dependent. Which means that the function which got `function(string $foo){}` in place of `callable(int)` could itself be strict-mode and when it would call this function it would raise a TypeError. On Mon, 23 May 2016 20:05:50 +0300, Bob Weinand <bobwei9@hotmail.com> wrote:

Markus Fischer

10 years ago
Hello Nikita, On 23.05.16 21:27, Nikita Nefedov wrote:
> When you pass an `int` to a `string` type parameter in weak mode > it's being coerced to the needed type (not just directly passed). > > This is quite complex, because you'd need to copy zend_function > and all its members (without changing the original zend_function). > > I would love to support it but all in all it comes down to > implementation here for me, so I'd rather do it in a separate > RFC.
I like the RFC, however I do not like this behavior. This would behave differently then how current weak/strict mode works. I was never a fan of the weak mode to begin with, but now introducing something which deals in the area of argument passing and not behaving to what a 2/3 majority agreed is IMHO a no-go. The current RFC does not even mention that. Maybe it's not feasible anyway, but in any case I'm missing input/discussion in this area. - Markus

Nikita

10 years ago
On Mon, 23 May 2016 22:59:10 +0300, Markus Fischer <markus@fischer.name> wrote:
> Hello Nikita, > > On 23.05.16 21:27, Nikita Nefedov wrote: >> When you pass an `int` to a `string` type parameter in weak mode >> it's being coerced to the needed type (not just directly passed). >> >> This is quite complex, because you'd need to copy zend_function >> and all its members (without changing the original zend_function). >> >> I would love to support it but all in all it comes down to >> implementation here for me, so I'd rather do it in a separate >> RFC. > > > I like the RFC, however I do not like this behavior. > This would behave differently then how current weak/strict mode works. > > I was never a fan of the weak mode to begin with, but now introducing > something which deals in the area of argument passing and not behaving > to what a 2/3 majority agreed is IMHO a no-go. The current RFC does not > even mention that. > > Maybe it's not feasible anyway, but in any case I'm missing > input/discussion in this area. > > - Markus >
Morning Markus, Well, those strict/weak modes were initially invented for basic types, not for composite types so I'd question if their support is needed at all... For example regardlessly of what mode your file in, if you implement an interface with `function foo(int $b)` you'd always have to implement it with `int`, and not `float`.

Dan Ackroyd

10 years ago
Hi Nikita, On 23 May 2016 at 15:27, Nikita Nefedov <inefedor@gmail.com> wrote:
> With this message I'd like to go to vote > with the Callable prototypes RFC targeted at 7.1: > https://wiki.php.net/rfc/callable-types
To explain my vote - I think we definitely need to be able to specify the signatures of callables in PHP, and I would very probably support either a 'typedef' RFC, or an expansion of current functional type (i.e. invokables) to be easier to use with functions, but I just couldn't possibly vote for an RFC that means people would sometimes write code similar to this: function foo(int $a, int $b, callable(callable(callable(callable(int, int):int $zebranky, int):int $pik, int):int $fot, int):int $zot): int { return $zot($a, $b); } to use the feature of the RFC. I realise that is an extreme case, but I think that splitting where a type is defined (with a name) and the place where it is used to be separate places, is one of the things that the design of PHP got right. It makes it easier to read code, even if it makes the language feel less 'powerful' than other languages. cheers Dan

Nikita

10 years ago
On Tue, 24 May 2016 02:24:12 +0300, Dan Ackroyd <danack@basereality.com> wrote:
> Hi Nikita, > > On 23 May 2016 at 15:27, Nikita Nefedov <inefedor@gmail.com> wrote: >> With this message I'd like to go to vote >> with the Callable prototypes RFC targeted at 7.1: >> https://wiki.php.net/rfc/callable-types > > To explain my vote - I think we definitely need to be able to specify > the signatures of callables in PHP, and I would very probably support > either a 'typedef' RFC, or an expansion of current functional type > (i.e. invokables) to be easier to use with functions, but I just > couldn't possibly vote for an RFC that means people would sometimes > write code similar to this: > > function foo(int $a, int $b, > callable(callable(callable(callable(int, int):int $zebranky, int):int > $pik, int):int $fot, int):int $zot): int { > return $zot($a, $b); > } > > to use the feature of the RFC. > > I realise that is an extreme case, but I think that splitting where a > type is defined (with a name) and the place where it is used to be > separate places, is one of the things that the design of PHP got > right. It makes it easier to read code, even if it makes the language > feel less 'powerful' than other languages. > > cheers > Dan
Morning Dan, I think we definitely would need type aliases. But I also hoped for union types to get in, so that after these two we could have another RFC that would do typedefs for callables and for unions. Doing typedefs for callables only seems too narrow. People would probably vote 'no' for that RFC because apart from creating new syntax for callables it would also create a flavour of typedef that works in limited scope only. And that seems completely fair, typedef is another feature. And even with typedefs I see no reason not to support inline callable prots. Do you remember 'Arbitrary expressions' RFC? I look at it just like at expressions, and I think there's a need for any possible type to be expressed directly in a parameter typehint. Making code more readable would be a responsibility of a user who would decide whether he needs to use function signatures or not. Regarding Joe's functional interfaces, I wish they were passing vote. They can be a nice addition (and a pretty simple one) to the language. I also think they are not related to this RFC, as their usage is completely different... What I am aiming at with this (and possibly next) RFC is to make function composition work better and functional interfaces don't touch this subject at all. The fact that function needs to opt-in and the fact that the signature of a function needs to be completely same as in interface (so no variance) makes it useless in cases where callable prototypes should be used instead.
> I realise that is an extreme case, but I think that splitting where a > type is defined (with a name) and the place where it is used to be > separate places
I think so too but "callable(int): int" is not defining a type, it is using it. On the other hand "callable" here is a type constructor. If we would have generics of some form this would be similar to: "Function<int, int>" where you don't define a type but you create it passing "int, int" arguments to a type constructor (or a template, or a generic class) which results in a type in the end. When you get parametrized types there's a natural need for parameterizing them in place...

Nikita

10 years ago
Evening internals, The vote for Callable prototypes has been declined with 18 votes in favor and 19 against adding the feature. Thanks everyone for participating, we will work on problems raised in the discussion and see if it meets people's expectations in 7.2