RFC: Functional Interfaces

php.internals

Joe Watkins

10 years ago
Morning Internals, Please review the following RFC: https://wiki.php.net/rfc/functional-interfaces An implementation is provided, and is testable on 3v4l. Review of the implementation from those of you that do that would be good :) Cheers Joe

Rowan Collins

10 years ago
Joe Watkins wrote on 18/04/2016 11:22:
> Please review the following RFC: > > https://wiki.php.net/rfc/functional-interfaces > > An implementation is provided, and is testable on 3v4l.
This sounds like a really nice feature. As well as using functions to implement existing interfaces, it gives a nice way of typehinting the requirements of a callback without forcing users to create an object. I must admit to being a bit confused by the term "functional interface" at first, though, because "functional" has so many different meanings - is this a term used elsewhere, or is it up for debate? In particular, the error message "Fatal error: cannot implement non functional interface IFoo" makes me wonder what's "non-functional" about the interface. It would be clearer to me if it was more specific and avoided the jargon, e.g. "Anonymous function cannot implement interface with more than one method". The error "functional interface cannot implement self" also seems inconsistent with the others - here "functional interface" refers to the *implementation*, not the interface. It might be clearer to just say "cannot implement non interface self" anyway - there's nothing actually special about "self" here, it's just another example of trying to use a class as an interface, which is illegal whatever the implementation. These are of course minor details, and the rest of the feature as described sounds great. :) Regards,
-- Rowan Collins [IMSoP]

Joe Watkins

10 years ago
Morning,
> I must admit to being a bit confused by the term "functional
interface" at first, though,
> because "functional" has so many different meanings - is this a term
used elsewhere,
> or is it up for debate?
The terminology is borrowed, Java 8 uses lamdas to implement specially annotated interfaces, it refers to them as functional interfaces. The other term is SAM ... but it feels a bit strange to give code a people name, right ? :) Single Abstract Method Interface Implementations is just ... too much ... I find it to be a pretty concise description, although the wording of the errors is definitely flexible. The "non thing" wording was only chosen because it's used elsewhere in the core, "non static" and so on. It just feels a bit strange to say or think "functional interface", but once you get over that ... As far as anyone who is not familiar with the workings of PHP is concerned, what they have is a function that implements an interface, functional interface is a pretty good description of that. Cheers Joe On Mon, Apr 18, 2016 at 12:34 PM, Rowan Collins <rowan.collins@gmail.com> wrote:

Rowan Collins

10 years ago
Joe Watkins wrote on 18/04/2016 12:53:
> Morning, > > > I must admit to being a bit confused by the term "functional > interface" at first, though, > > because "functional" has so many different meanings - is this a > term used elsewhere, > > or is it up for debate? > > The terminology is borrowed, Java 8 uses lamdas to implement specially > annotated interfaces, it refers to them as functional interfaces. >
Thanks, I figured that was probably the case.
> As far as anyone who is not familiar with the workings of PHP is > concerned, what they have is a function that implements an interface, > functional interface is a pretty good description of that.
I guess what's confusing is that the new feature happens entirely at the implementation end, but the term describes the interface itself. What we actually have is a functional *implementation* of a normal, non-annotated interface; but the interface is *implicitly* functional because of having only a single method. So when the error says you have a "non functional interface", it's not clear what you need to do about it. I like the implicit definition, though, because it reduces the need for a forwards compatibility break (libraries can support old versions of PHP, but their users still take advantage of the new feature if they want). So I'm not really sure how to make the definition of "functional interface" clearer, other than making sure we write some decent documentation. :) Regards,
-- Rowan Collins [IMSoP]

Fleshgrinder

10 years ago
+1 for the feature, very nice syntactic sugar to solve some edge cases. I agree with Rowan, i also do not like the name and propose to call them *closure interfaces*. There are already various synonyms for closures: - anonymous function - lambda function - callback* However, the strongest argument to call them *closure* is the fact that the the class in PHP itself is actually called *\Closure*. I would even go as far as saying that the documentation should be updated to always use that word everywhere.* The fact that only closures can implement such interfaces strengthens the reason to call them as proposed in my opinion. Just because Java calls them /functional/ does not mean that we should. Regarding error messages: Fatal error: cannot implement non functional interface IFoo in /in/65W6i on line 7 Fatal error: cannot implement nonclosure interface IFoo in /in/65W6i on line 7 Note on grammar: http://www.chicagomanualofstyle.org/qanda/data/faq/topics/HyphensEnDashesEmDashes/faq0079.html *) There is of course the situation of the various other ways to declare the so called *user functions* (/call_user_func()/ and /call_user_func_array()/) and those should always be referred to as *callback*; imho.
-- Richard "Fleshgrinder" Fussenegger

Stas Malyshev

10 years ago
Hi!
> Please review the following RFC: > > https://wiki.php.net/rfc/functional-interfaces > > An implementation is provided, and is testable on 3v4l.
This looks like a rather narrow case already covered by anonymous classes. Am I missing something? What is the added value of it that can't be done now?
-- Stas Malyshev smalyshev@gmail.com

Marcio Almada

10 years ago
Hi! 2016-04-18 15:17 GMT-04:00 Stanislav Malyshev <smalyshev@gmail.com>:
> Hi! > > > Please review the following RFC: > > > > https://wiki.php.net/rfc/functional-interfaces > > > > An implementation is provided, and is testable on 3v4l. > > This looks like a rather narrow case already covered by anonymous > classes. Am I missing something? What is the added value of it that > can't be done now? > > -- > Stas Malyshev > smalyshev@gmail.com > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php >
I haven't decided if I'm in favor of this feature yet but I asked the same questions as you when the pull request landed. This gist served as a good illustration of possible benefits https://gist.github.com/marcioAlmada/12ef23038bcf5acaa2e2f6b808c968d3. Boilerplate measuring apart, it seems logical that a closure should be able to fulfill an interface with a single method. About the applicability, I'm under the same impression as you, the use case is probably narrow, perhaps because anonymous classes is a newly added feature and wasn't explored enough. Cheers. Márcio Almada.

Sara Golemon

10 years ago
On Mon, Apr 18, 2016 at 12:17 PM, Stanislav Malyshev <smalyshev@gmail.com> wrote:
> Hi! > >> Please review the following RFC: >> https://wiki.php.net/rfc/functional-interfaces >> An implementation is provided, and is testable on 3v4l. > > This looks like a rather narrow case already covered by anonymous > classes. Am I missing something? What is the added value of it that > can't be done now? >
I thought Joe covered that question pretty thoroughly by his countable examples. The version using anonymous classes certainly *works*, but it's significantly more boilerplate code and has a higher cognitive overhead compared to the functional interface example which is concise and to the point. That said, I'm not sure what I'd use it for, but as syntactic sugar goes it's quite sleek. -Sara

Dmitry Stogov

10 years ago
<?php interface IFoo { public function method() : int; } $cb1 = function () implements IFoo : int { return 42; }; $cb2 = new class implements IFoo { function method() : int { return 42; } }; ?> Are $cb1 and $cb2 going to be the same (do the same)? Is this just a new syntax sugar, or a really new feature? According to implementation, I think, you shouldn't extend "zend_closure" with "interface". If this is a sugar, lets implement it as a sugar (without VM changes). But may be I didn't understand the idea at all :) Thanks. Dmitry. ________________________________________ From: Joe Watkins <pthreads@pthreads.org> Sent: Monday, April 18, 2016 13:22 To: PHP internals Subject: [PHP-DEV] RFC: Functional Interfaces Morning Internals, Please review the following RFC: https://wiki.php.net/rfc/functional-interfaces An implementation is provided, and is testable on 3v4l. Review of the implementation from those of you that do that would be good :) Cheers Joe

Joe Watkins

10 years ago
Morning Dmitry, In your example, they will do the same thing. Anon classes don't have lexical scope, I have a patch for that, and an RFC, I'm just tidying it. Even when anon classes do have lexical scope, there is value in being able to implement these kinds of interfaces using only a function. The changes to the vm are restricted to a few lines, that's not seriously a problem, is it !? I don't see where I extend closure with an interface ...
> zend_do_inheritance_ex(type, zend_ce_closure, 1); > zend_class_implements(type, 1, interface);
Cheers Joe On Tue, Apr 19, 2016 at 1:01 PM, Dmitry Stogov <dmitry@zend.com> wrote:

Joe Watkins

10 years ago
Oh, it's confusing text in the RFC, using the word "Interface" ... that's meant as a placeholder for InterfaceName. Cheers Joe On Tue, Apr 19, 2016 at 1:50 PM, Joe Watkins <pthreads@pthreads.org> wrote:

Dmitry Stogov

10 years ago
On 04/19/2016 03:52 PM, Joe Watkins wrote:
> Oh, it's confusing text in the RFC, using the word "Interface" ... > that's meant as a placeholder for InterfaceName. > > Cheers > Joe > > On Tue, Apr 19, 2016 at 1:50 PM, Joe Watkins <pthreads@pthreads.org > <mailto:pthreads@pthreads.org>> wrote: > > Morning Dmitry, > > In your example, they will do the same thing. > > Anon classes don't have lexical scope, I have a patch for that, > and an RFC, I'm just tidying it. >
OK. so this is not just a syntax sugar, but if you implement lexical scope for anonymous classes it's going to be sugar. Right? May be it's better to start from classes first?
> > Even when anon classes do have lexical scope, there is value in > being able to implement these kinds of interfaces using only a > function. >
What value? just shorter syntax? or anything else?
> > The changes to the vm are restricted to a few lines, that's not > seriously a problem, is it !? >
I hope no. I didn't understand all details of proposal and I didn't take a deep look into the code.
> > I don't see where I extend closure with an interface ... >
I meant new field zend_execute_data *interface in zend_closure structure. I thought the same might be implemented through anonymous classes. Thanks. Dmitry.

Joe Watkins

10 years ago
> What value?
Yes, shorter, more concise (from the programmers perspective) syntax. Right now, if you do a lot of work with Closures, you have to verify their interface, the value in having the engine do it for you seems really obvious. But also ...
> I thought the same might be implemented through anonymous classes.
An anonymous class cannot be rebound, they are obviously not the same thing.
> I didn't take a deep look into the code.
I'll wait until you have, I think :) Cheers Joe On Tue, Apr 19, 2016 at 2:22 PM, Dmitry Stogov <dmitry@zend.com> wrote:

Dmitry Stogov

10 years ago
For me, the proposal is too complex. I prefer intuitive concepts, that I may use without rereading manual again and again. For this one, I even can't imagine a natural (not over-designed) use case. But this is my personal opinion, and I may be wrong :) I'll review the code later. Thanks. Dmitry. On 04/19/2016 04:29 PM, Joe Watkins wrote:

Rowan Collins

10 years ago
Dmitry Stogov wrote on 19/04/2016 15:12:
> I prefer intuitive concepts, that I may use without rereading manual > again and again. > For this one, I even can't imagine a natural (not over-designed) use > case.
The use case that came to my mind is kind of the other way around from "syntax sugar for anonymous classes" - providing a contract for what a callback should look like. In essence, it's an alternative to specifying closure type with a generic-like syntax, as proposed at https://wiki.php.net/rfc/callable-types Say you currently have this: public function registerCallback(callable $callback) { ... } You could instead specify what the callback needs to accept and return: interface EventCallbackInterface { public function __invoke(Event $event): boolean; } public function registerCallback(EventCallbackInterface $callback) { ... } Using __invoke as the method name may or may not be a good idea, but it interestingly means I don't need to change the code that runs the callback, I can just use $callback($some_event); Then if someone wants to use a complex object, they can; and if they just want to use a closure, it has to meet the contract. Regards,
-- Rowan Collins [IMSoP]

Dmitry Stogov

10 years ago
On 04/19/2016 05:30 PM, Rowan Collins wrote:
> Dmitry Stogov wrote on 19/04/2016 15:12: >> I prefer intuitive concepts, that I may use without rereading manual >> again and again. >> For this one, I even can't imagine a natural (not over-designed) use >> case. > > The use case that came to my mind is kind of the other way around from > "syntax sugar for anonymous classes" - providing a contract for what a > callback should look like. In essence, it's an alternative to > specifying closure type with a generic-like syntax, as proposed at > https://wiki.php.net/rfc/callable-types > > Say you currently have this: > > public function registerCallback(callable $callback) { ... } > > You could instead specify what the callback needs to accept and return: > > interface EventCallbackInterface { > public function __invoke(Event $event): boolean; > } > public function registerCallback(EventCallbackInterface $callback) { > ... } > > Using __invoke as the method name may or may not be a good idea, but > it interestingly means I don't need to change the code that runs the > callback, I can just use $callback($some_event); Then if someone wants > to use a complex object, they can; and if they just want to use a > closure, it has to meet the contract.
callable-type is much simpler solution for this use-case. Usage of Interfaces to check function prototypes is a bit tricky, but yes, this is a possible use-case. Do you like to work with framework, that use this trick for every callback? Thanks. Dmitry.

Rowan Collins

10 years ago
Dmitry Stogov wrote on 19/04/2016 15:48:
> callable-type is much simpler solution for this use-case. > Usage of Interfaces to check function prototypes is a bit tricky, but > yes, this is a possible use-case. > Do you like to work with framework, that use this trick for every > callback?
If you don't use __invoke as the function name, you can have a single class implement multiple contracts, e.g. to register for more than one event type with different signatures. I guess if you had callable-types *and* union types, you could get the best of both worlds: interface UserEventCallbackInterface { public function handleUserEvent(UserEvent $event): boolean; } public function registerCallback(EventCallbackInterface|callable(Event): boolean $handler) { ... } The implementation has to be a bit fiddlier here, because you need to sometimes call $handler->handleUserEvent($event) and sometimes $handler($event); with a functional interface, the engine makes $handler->handleUserEvent($event) work for you. The cost is that the call site has to be a bit more verbose to specify the interface, rather than implicitly meeting the contract. Regards,
-- Rowan Collins [IMSoP]