-
-
Notifications
You must be signed in to change notification settings - Fork 633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
In depth Closure definitions. #830
Comments
Hmm.. interesting. We have been talking about a concept know as Inline DocBlocks or defining inline types using an |
Yes whatever comes out of this could be used for more than just Closure definitions, but any open ended type that is being used in a structured manner. Looking forward to your elaboration. |
What about something like this? @param Func<string, string, string> $concat String concatenator @param Action $logger Message logger Action would be a closure that would accept every given type as input parameter and return nothing and function would accept all the given types (sans the last one) as input and would return the last type given. The syntax is taken from C#, where it is really popular and useful thing: http://simpleprogrammer.com/2010/09/24/explaining-what-action-and-func-are/ |
I am afraid that Func and Action do not cover the requested functionality since your are not able to define which parameters and return value is associated with the Closure. One of the options that I am thinking of is to be able to declare Closures in a PHPDoc comment. For example:
This is just an example and even the tag name could very well change. This will allow many more things such as declaring a stdClass substitution in case of SOAP, struct definitions for associative arrays and alike. Obviously this is a big feature and more research time is needed; it will be after 2.0 that I will have a lot of time to investigate this deeper |
Block syntax
This could even be shortened to an inline in the case of closures that do not take arguments.
And would read fluidly in the case of curried functions
The one down side of this method is that descriptions then become awkward.
Declaration format has the benefit of allowing a less awkward description:
But then you are getting in to repetition of the description. The description should really be bound to the declaration, but again we run in to an awkward syntax. |
Nah. The description of a returned value belongs at the method's return tag. Declarations are no different, assuming you declare them just once in the file's docblock. Whether we allow "inline" declarations is debatable, but we MUST have non-inline declarations at minimum, as this is the only way to make circular returns. (Side note, we've already talked about inline docblocks... we may have to leverage that syntax if there's going to be tags inside tags) e.g. /**
* @declare function forward {{
* @param int $speed
* @param string $text
* @return callable<back>
* }}
* @declare function back {{
* @param int $speed
* @param string $text
* @return callable<forward>
* }}
*/
/**
*
* @param int $direction
*
* @return callable<back>|callable<forward>
*/
public function getMover($direction)
{
} can't be declared inline. |
I am definitely not opposed to an inline syntax as an alternative to a declaration, for example:
would be the same as
As @boenrobot correctly spots this is a re-use of the Inline PHPDoc idea that has been floating for some time (at least in my mind), and is the declaration necessary for element re-use. And re-use should be limited by scope, thus a declare in a method is only applicable in that method but a declare at the class is applicable in the class and every property, method and constant. With regards to the description, this should go inline with the tags, as a complete PHPDoc definition. |
You know... come to think of it... maybe we're going this the wrong way... at least as far as closures are concerned... How about we make the value between "<" and ">" be the name of any PHP function or method (when the primary type is "callable" that is)? If you use anonymous functions (which is obviously the primary use case), you can make a special PHP file that only phpDocumentor reads, and which contains non-anonymous such declarations, with docs and everything. As far as IDEs and QA tools are concerned, those functions do exist in your special file, but as far as the runtime is concerned, it's actually calling an anonymous function that happens to have the exact same prototype and purpose. phpDocumentor and IDEs already support finding functions within conditions, so making this special file "safe for inclusion" is a piece of cake: <?php
//Suppose "closures.php" in the project root (i.e. not meant for inclusion in actual use)...
if (false) {//... but just in case
/**
* Closure's short description
*
* Closure's long description.
*
* @param int $param1
* @param string $param2
* @return bool TRUE on success, FALSE on failure.
*/
function back($param1, $param2)
{
}
} <?php
//Actual file to be included in real use
/**
*
* @param int $direction
*
* @return callable<back>
*/
public function getMover($direction)
{
} This should actually be FAR more trivial to implement than the above plans (since the only thing missing is the recognition of the chevrons' value and the creations of links for it). Thoughts? |
Closure definitions in a separate file is untenable. PHPDocumentor is effective because all descriptions stay localized to the code they are describing. Additionally closures are "one offy" by nature and having to document them elsewhere is just not in the spirit of the code. |
True, but the "callable" type isn't necessarily such - you're allowed to also return a string that is the name of an existing non-anonymous function or an array with an object/method, which can then be called (hence "callable"). |
I do not feel much for this suggestion because I think that
|
|
I feel that userland PHP code should not be required to satisfy documentation purposes; in addition I believe it will be too much hassle for the 90% use-case (which, I believe, is one-off calls and not re-usable calls). |
Also, the @declare does not add much complexity if Inline PHPDoc and Generics find their way into the syntax as it would only be an additional tag with elements known in other parts of PHPDoc |
The "call" and/or "bind", sure - the 90% use case in indeed a one time call/bind. The question is about the "declaration" though - is a "one time prototype" the 90% use case? I'm not so sure. Many projects have the same prototype of callback in multiple places - the runtime may "bind" to this callback just once and/or call it just once, but when the same prototype is being available in multiple places, having to re-declare it multiple times becomes cumbersome. Case in point - JavaScript events (and PHP systems that may try to emulate a similar kind of coding style) - most applications bind to events just once, but all events accept a single "event" object, and return a bool value that tells whether the event chain continues for this triggering. Now, sure, many events have multiple specific properties, so in a similar PHP application, you'd want to have multiple prototypes, but you can also find many events that do share the same prototype (e.g. in JS' case - onkey* events with one, onmouse* with another, etc.). |
Why would a method similar to i.e.
or
if you'd want to be more explicit that it is a declaration. |
OK, that one might work... with both syntaxes being allowed - the first when a prototype of a real function/method is addressed, and the latter when a declaration in a class is addressed. But if we're going to go on such kind of a route, I guess we should also go the extra mile and say file-level declarations are global across the entire project run - and let them be namespaced (and be relative to the root by default, unless a slash is present - like how tags are). e.g. /**
* Assume a file level docblock...
*
* @declare callable \My\Project\myCallable {{
* Addressed the first and second time.
* ...
* }}
*
* @declare callable myCallable {{
* Addressed the third, forth and fifth time
* ...
* }}
*/
//Later...
/**
* @return callable<\My\Project\@myCallable> Or maybe...
*/
/**
* @return callable<@\My\Project\myCallable> ... to avoid inconsistency between declaration and addressing.
*/
//...
/**
* @return callable<@myCallable> Same as...
*/
/**
* @return callable<\@myCallable> ... or rather ...
*/
/**
* @return callable<@\myCallable>
*/ One may argue the "@" is syntactically confusing (due to it also being used for tags), but I don't see a better character as an alternative. |
I'm curious why callables in general are being drawn in to this discussion. PHP has 3 types of callables:
Item 1 does not currently have a format for documentation. 2 and 3 can already be documented with normal doc blocks. Am I missing something? |
Woops sorry about that, accidentally hit the wrong button. |
@mvriel Func and Action do cover it, I just forgot to put it into the code block (I'm not all that familiar with GitHub, I just it once in a while) and not have the angle brackets removed. To reiterate on my example (with complete code samples, although kinda made up ones). /**
* @param Action<string> $fn Function to do with every element of array
* @param array $in
*/
function doForeach($fn, $in)
{
foreach ($in as $item) $fn($item);
}
/**
* @param Func<int, int> $fn Function to apply to every element of array
* @param array $in
*/
function map($fn, $in)
{
$out = array();
foreach ($in as $item) $out[] = $fn($item);
return $out;
} Of course it would be better to use some "more" phpesque names, but this kinda of apporach seems best for me (but I may be kind of biased because I switch between C# and PHP back and forth) -- especially the recognization between callback that returns a value and one that does not. Additional information in documentation block about anonymous function (apart from typing) -- maybe description for parameters would make sense, but naming is pointless (because when you expect some anonymous function, you only care about types of parameters; not names). The description could help though, so I'm not againts that. Side-note: The angle brackets look like "generics" though and I'm not sure about phpDoc's stance on Generics. I personally, when developing, would find them as a great addition, but I'm not sure about other people; is there an issue about generics which was rejected/is currently under discussion? |
Yeah, about 3... while you can declare "callable" as a type (which will let you accept/return a named function), there's no way to specify the expected prototype of a named function/method, just as there's no way to specify prototypes for anonymous functions either. Named functions are on equal footing with closures as far as documentation about them is concerned.
No, since PHP doesn't really need generics, due to its loose type-ness. There are other C# features that have been brought up, and ultimately taken down (in some cases, unfortunately so, IMHO), such as get/set syntax for properties for example. |
@boenrobot: I meant Generics in phpDoc, not in language itself. |
@pajousek No, there aren't generics in phpDoc, which is why we're considering this particular syntax. Considering PHP's nature, I'm not sure how "generics" would look in phpDoc terms. |
👀 |
Is this still being considered? (Type1, Type2) -> ReturnType For PHP it would probably make more sense to use the /**
* @param (Type1, Type2): ReturnType $callback
*/ I could see how this would be difficult to implement for ambiguity reasons though. |
Or how about: JSDoc (and Google Closure) use something similar except in curly braces @param {function(string, number):boolean} mycallback my description I'd just like to get my IDE to stop giving me warnings for unrecognized properties and methods because it doesn't know what the parameters of the callable function are. |
Is this still in consideration? This has long been a wish of mine. Any syntax will do, tho @iluuu1994 / @sl1331 suggestion seems sane in a PHP context. |
Yes it is, but most likelly it will be implemented in phpdocumentor v3. If someone is able to create a pr for this that might speed up the progres. |
Sorry if out of topic. Of course permanent solution (with new syntax) is absolutely better. |
Doc blocks do not currently support in depth Closure definitions. Only a shallow definition is possible with the type definition:
Use of closures is becoming more prevalent in modern PHP, so a more in depth format is needed.
Some possible formats
Single line definition. Double brackets are used to denote a sub definition.
Inner doc block definition. Ending comment statement is excluded on the inner doc block.
The text was updated successfully, but these errors were encountered: