Closed mvriel closed 10 years ago
Looks good to me.
I have just added a section on Generics and refactored the ABNF for types (if only because that was a weird mix between EBNF and ABNF). A proof read is welcome!
(Disclaimer: this was written at 11 o'clock without proper proofreading myself, please be gentle ;))
Nice; some thoughts...
Firstly, I would really like to capture the generic as a general type bound. If I have MyObject<int>
I should be able to have /** @generic int T */
on MyObject
where I can use T
throughout the class. This makes it more general and not specific to Traversable/ArrayAccess implementations
Secondly, I'd like the same type of type parameters to be local to, e.g.
/**
* @generic string[]|string T
* @param T $input
* @return T
*/
An alternative to @generic
can be a colon-suffixed type-variables if we don't want to waste another line w/ a new tag, e.g.:
/**
* @param T:string[]|string $input
* @return T
*/
I know this is asking a lot and can lead to other requests for variance in the type bounds and what not, but it at least doesn't limit this to PHP collections and arrays. Just some food for thought.
@cretz Interesting thought and I see some overlap with inline element definitions (https://github.com/phpDocumentor/fig-standards/issues/7).
My main concern is: how will this improve documentation? The intention of this PSR is purely to add documentation to PHP and not any behavior.
I can imagine that being able to provide a named alias to a "Type" is something that may be desirable but, I guess, that would more be an additional consideration for the inline element definitions.
The only way it really improves documentation is it narrows the type for the caller. So instead of seeing mixed everywhere or a bunch of |'s, you can almost define types and/or type aliases in documentation. I do believe it's a tad overcomplicated for what PHPDoc does, but I fear restricting the ability to provide type parameters only to arrays and traversable/arrayaccess.
That generics-inspired syntax is what we use in TYPO3 Flow for documentation and also type validation / property mapping. So a big +1 for having that "standardised" for documentation and hopefully even in a PSR. Yay!
It's not defined what collection is in PHP. Should a class implement ArrayAccess
to be a collection?
My main concern is: how will this improve documentation?
It will improve documentation of libraries, which are documented for the purposes of other developers consuming those libraries - generics are a way to describe type-relationships, which is something that exists conceptually in almost any language. Being able to describe them "mathematically", in a standardized expression form, is much better than having to describe them in english, in a comment, where no documentation generator (or IDE or annotation library) can derive any meaning from them.
This is no different from any other type of annotation that describes any other specific aspect of code - technically, you could have an annotation standard that consisted of @
followed by plain text and nothing else; but the documentation you could generate from that would not be very useful, so we use more specific types of expressions to make sure the documentation is human readable and machine parsable.
Generics have become a common way to describe type relationships, that many developers are comfortable with from other languages already.
How does the ability to concisely describe type-relationships not improve documentation? :-)
The intention of this PSR is purely to add documentation to PHP and not any behavior.
Annotations should never add behavior to anything - they are purely metadata.
But you have to look at annotations as more than just documentation these days - your PSR defines something that is already wide spread and heavily used in the community, and it's not safe to ignore the fact that people rely on annotations for IDE support (inspections, automated refactoring, etc.) and consuming metadata at run-time has also become commonplace with mainstream frameworks like Symfony adopting the idea in recent years.
In addition: if this PSR becomes approved, there is a very high likelihood that other documentation generators, IDE developers, annotation library developers (such as myself) and developers of any tool that relies on annotations, will adopt and implement the standard.
PSR is the closest thing PHP has to a community standard.
I see it as absolutely crucial that a community standard consider the needs of the entire community, to the extend that doing so is not a hindrance.
PSR standards after all are coding standards, and as such need to cater to more than just documentation.
Here's a "real world" example of a repository pattern, using a (suggested) new @template
annotation (as per Google Closure Compiler annotations) to define the type arguments:
interface IEntity
{ ... }
/**
* @template TEntity : IEntity the type of Entity hosted by this Repository
*/
class Repository
{
/**
* @param string $type
*/
public function __construct($type)
{ ... }
/**
* @param int $id
* @return TEntity
*/
public function get($id)
{ ... }
/**
* @param int $id
* @return TEntity[]
*/
public function find($criteria)
{ ... }
/**
* @param TEntity $entity
*/
public function delete($entity)
{ ... }
}
And a brief example of usage:
/** @type Repository<User> */
$users = new Repository(User::class);
// implicit @type User by inference:
$user = $users->get(123);
// implicit type-check in IDEs:
$users->delete($user);
It's really not complicated - you're just defining your own type-aliases with @template
similar to built-in aliases like self
or static
, but actually much simpler to resolve if you had to; which, for documentation purposes, you probably don't even need to.
I used a fictive syntax in my example, in which I assumed you would optionally be able to specify what kind of type is required as type-argument - this should expect a regular type-declaration as per @type
etc., but should be optional.
We would need a syntax for providing default type-arguments as well, in order to support both array<TElement>
and array<TIndex, TElement>
- or, alternatively, as e.g. TypeScript has, a non-generic syntax for index and element types.
If for any reason you're unwilling to include full support for "real" generics, alternatively, I would suggest you abandon the proposed generic-style syntax, in favor of some other syntax to designate index and element types specifically. (Other languages and annotation styles have syntax specifically to deal with index and element types - it does not typically look like generics, as this is fundamentally a different thing than generics entirely.)
PS: if you think having type-checks for type-arguments is too complicated and "mathematical", I wouldn't hold that against you - I would be quite happy with a simple @template [type_alias] {comment}
syntax, as this is much simpler and covers most uses just fine.
Ideally we should be able to constrain the template-type, e.g. @template [type_alias] {: type} {comment}
, and from your perspective this doesn't need to be anything other than documentation, though (in the example above) it wouldn't suck to be able to click through from the template-type to IEntity
in the documentation.
But I would be happy either way - as long as we don't pollute or confuse the basic concept of generics.
PPS: if you wanted to support (either or only) index and element declarations, I would suggest building on the existing type-syntax which already has a syntax for indicating collections... You can actually have ambiguous types with the current proposed syntax, e.g. array<Foo>
is identical to Foo[]
- since array<Foo>
implies generics, I would suggest building on the existing annotation-syntax for arrays ([]
token) instead, e.g. Foo[]
which already works rather than array<Foo>
, and e.g. Foo[string]
which builds on the existing array syntax, rather than array<string,Foo>
which implies generics. For custom collection-types, e.g. a class Collection
as per your examples, you could declare those as Collection|Foo[string]
which is much closer to what you would do for collection-types presently, without the ability to specify the index-type, e.g. Collection|Foo[]
.
As said, I would love to also have generics as part of the specification - but the key issue for me right now, is to make sure we don't end up with a standard that looks like generics but doesn't work like it.
I apologize for the bombardment of messages, but this is really important to me. I'll shut up now :-)
Hi @mindplay-dk, your comments have given me something to ponder and research. Hence I have not responded yet. I am still in the process of research and will get back to this asap
Would you like me to propose syntax for @template
, update the ABNF, and submit a PR?
@mindplay-dk would you mind posting your proposed syntax here with a little bit of elaboration? It looks a lot like the @define
tag that is proposed in another issue but I want to be certain whether we are talking about the same thing. Thank you!
Okay, let's go by example.
Let's say we have a final class View
that may contain a view-model of any type:
/**
* @template TModel:ViewModel the type of view-model
*/
class View
{
/**
* @var TModel view-model
*/
public $model;
/**
* @var string absolute path to a PHP template
*/
public $template;
/**
* @var TModel $model the view-model
* @var string $template absolute path to a PHP template
*/
public function __construct($model, $template)
{
$this->model = $model;
$this->template = $template;
}
public function render()
{
require $this->template;
}
}
// a sample view-model:
class User
{
public $name;
}
// ----8<---- creating a model:
$user = new User;
$user->name = 'Mike';
// -----8<----- creating a View:
$view = new View($user, __DIR__.'/templates/user_presence.php'); // inferred type View<User>
$user = $view->model; // inferred type User (via TModel)
This is basically identical to generics and type-arguments in pretty much any other language that supports generics:
@template TModel:ViewModel
which says, "there's a type alias TModel
used to define a type that extends ViewModel
". @var TModel
on the $model
member.@param TModel $model
means you're passing $model
as the argument, and type-of($model
) as TModel
.Type syntax would need an extension to fully support type-arguments, e.g. View<User>
and View<Whatever>
are two different types, whereas View<>
is an open generic type.
You can define type-arguments for generic methods as well - a basic use case would be a factory class for the View
class we defined above:
class ViewFactory
{
/**
* @template TModel:TViewModel
* @param TModel $model the view-model
* @return View<TModel>
*/
public function createView($model)
{
return new View($model, $this->findTemplate($model));
}
/**
* @param ViewModel $model
* @return string PHP template path
*/
protected function findTemplate($model)
{
...
}
}
// creating a model:
$user = new User;
$user->name = 'Mike';
// creating a View using ViewFactory:
$factory = new ViewFactory();
$view = $factory->createView($user); // inferred type View<User> (via TModel argument)
In this case:
createView()
method has a type-argument TModel
.$user
, which has a known type User
, you're also passing the type-argument User
for TModel
.@return View<TModel>
annotation uses the type-alias defined for the method to define a variable return-type, which can be resolved/inferred from the type of arguments passed to the method when it's called.The scope of type aliases (within which they are valid and may be used) is different:
@template
annotations define type-aliases that can be resolved within the scope of the entire class.@template
annotations define type-aliases that are only valid within the scope of the function itself, e.g. for use in @param
and @return
annotations.If you understand generics (from another language like C# for example) there is nothing new or surprising here - in other words, I'm simply proposing this should work like generics in any other language that supports generics. We should not pollute the concept, invent something new, or take away from it, because generics are already well-established in other languages, and well-known to many programmers.
Conceptually, generics are already possible in PHP, and whether you are aware of it or not, you are most likely already writing or using something that can be described using generics, which is not conceptually a language feature; it's a formal way to describe type-relationships. (point in case: TypeScript, which fully supports generics, without leaving any trace of type-aliases or type-arguments in the compiled JavaScript source.)
I know this is may be a hard sell for you, Mike - because you may be thinking this would be very difficult to implement in PHP-DOC? I don't think you need to worry about that - documentation generators for other languages that support generics generally do not attempt to resolve generic types; they simply document the type arguments, and leave e.g. TModel
when used somewhere else, unmodified, as just a link to the type-argument description, which is dead easy to resolve. The larger burden rests on e.g. the Php Storm team, who would need to really infer/resolve local types and type-arguments recursively - but I'm pretty sure they've already dealt with generics in other languages (for example, they have TypeScript support already) and won't have any serious trouble supporting this.
Note that this involves very little actual new syntax: a new tag @template
with two arguments (the alias and a type specifier) plus the change to type-specifiers, permitting the use of <T1,T2,...>
as type-arguments. (and removing the collection-type syntax which is not generic.)
Hi @mindplay-dk,
This may not be as tough to sell as you'd think. It did cost me some effort to let go of the idea of the very loose generics-like syntax that is floating about to the idea of a more true form Generics (or templates).
In the mean time I have spent time contemplating your text and investigating how facebook's hack language is incorporating Generics. I think it is wise for us to follow suit with hack and keep a close eye on the arrayof RFC (https://wiki.php.net/rfc/arrayof).
The arrayof RFC might be a deciding factor in how PHPDoc should handle this situation so I am purposefully delaying this item until the Internals has made a decision on how to move forward with that. PHPDoc is a meta-language on top of PHP and should match their decisions as much as possible.
That RFC already failed with the [] syntax so go with the hack generics, that's what we'll be doing next with the RFC.— Sent from Mailbox for iPhone
On Sat, Mar 29, 2014 at 12:03 PM, Mike van Riel notifications@github.com wrote:
Hi @mindplay-dk, This may not be as tough to sell as you'd think. It did cost me some effort to let go of the idea of the very loose generics-like syntax that is floating about to the idea of a more true form Generics (or templates). In the mean time I have spent time contemplating your text and investigating how facebook's hack language is incorporating Generics. I think it is wise for us to follow suit with hack and keep a close eye on the arrayof RFC (https://wiki.php.net/rfc/arrayof).
The arrayof RFC might be a deciding factor in how PHPDoc should handle this situation so I am purposefully delaying this item until the Internals has made a decision on how to move forward with that. PHPDoc is a meta-language on top of PHP and should match their decisions as much as possible.
Reply to this email directly or view it on GitHub: https://github.com/phpDocumentor/fig-standards/issues/6#issuecomment-38999808
@philsturgeon I think I get where @mindplay-dk is coming from.
/**
* @type Collection<ValueObject>
*/
The above is only one part of the equation; it is essentially meaningless if the collection class is not defined as a Generic (class Collection<T> {}
) because ValueObject in the above example has no meaning.
It helped me a great deal to think this through and read the hack documentation on Generics (http://docs.hhvm.com/manual/en/hack.generics.php).
Because there are so much more concerns I am rather interested to see how a discussion on internals will go because using generics syntax to describe that something is an arrayof or collection is only one side of the story
Mark Chu-Carroll (aka goodmath) nicely summed up my feelings/concerns about this subject in this article which happens to pertain to Go:
The Go type system does not support generic types. They’re considering adding them at some point in the future, but for now, they don’t consider them necessary. Lowly programmers just don’t need parametrics, and it would clutter up the beautiful compiler to implement them.
Oh, but wait… Go really needs type-safe arrays. Well, that’s OK. Everyone provides arrays as a sort of special case – what language doesn’t have typed arrays, even if it doesn’t have any other parametric types? And we really want these cool things called slices – but they really need to be strongly typed. So we’ll let them be parametric. And maps – we really need a map type, which maps keys to values, and it really needs to be type-safe. So we’ll add a parametric map type to the language, by making it a special case built-in.
So: you can’t write parametric types – but they can. And that creates a very weird asymmetry to the language. Everything in Go is passed by value – except for the built-in slice and map types, which are passed by reference. Everything is allocated by “new” – except for the built-in slice and map types, which are allocated by “make”. It’s by far the biggest blemish in Go, and it’s absolutely infuriating.
Having "baked-in" generics only for certain specific types that somebody deemed "worthy" or "important" enough, is going to create the same weird symmetry in any language, or anywhere, in this case type-annotations.
The other side of the coin, is that, because PHP isn't statically typed, you can can create any kind of type relationship you can imagine - having type-annotations that are less expressive than the language itself is the caue of this asymmetry. TypeScript, for comparison, does support generics for type-annotations - it has to, because in JavaScript any kind of type-relationship is possible as well, the same as in PHP.
Simplified "baked-in" generics for certain specific types is going to do harm in the long run, since it's going to create more inconsistency and asymmetry if/when eventually real support for generics, for all types, is introduced.
Simplified "baked-in" generics for certain specific types is going to do harm in the long run, since it's going to create more inconsistency and asymmetry if/when eventually real support for generics, for all types, is introduced.
Exactly my sentiment, and is why I hate the idea that arrayof and generics are trying to be the same thing just because they are quite similar. Under the hood they can be the same, but they are not the same thing.
This is a conversation for internals though, not here.
My sentiment is: if you're waiting for that RFC then wait no more, because arrayof is not happening for 5.6 and I have no idea if it'll get in for 5.7, hopefully 6 (7?).
Make whatever choices you think make the most sense but Foo[] looks doubtful.
Make whatever choices you think make the most sense but Foo[] looks doubtful.
Agree, unless Foo[]
is an alternative syntax 100% identical and interchangeable with array<Foo>
- I'm fine with that (especially seeing how that is backwards compatible with current type-annotations) as long as no internals/concepts/APIs end up interpreting or describing Foo[]
as some magical/special type.
Yeah, both might not hurt. This conversation has given me the drive to go and fight for both further in PHP internals, because randomly deciding that array
is a generic class which automatically acts like a generic without the definition sounds f**king disgusting.
-- Phil Sturgeon
On March 29, 2014 at 5:07:26 PM, Rasmus Schultz (notifications@github.com) wrote:
Make whatever choices you think make the most sense but Foo[] looks doubtful.
Agree, unless Foo[] is an alternative syntax 100% identical and interchangeable with array
— Reply to this email directly or view it on GitHub.
randomly deciding that
array
is a generic class which automatically acts like a generic without the definition sounds f**king disgusting
I could understand your drive to take this to internals, if we were talking about a language feature, but we're talking about a type-annotation, which won't be enforced or checked, neither at run-time, nor at compile-time; ideally, it would be checked at design-time (in an IDE, or other tools) but again, it can't be enforced.
My point is, type-annotations are advice, for developers and tools - they are not an extension to the language, but a way to provide static type-hints that can't be expressed by the language itself. You know all this of course, so what I'm getting at is, we're defining a means of describing type-information and type-relationships. These relationships may exist in your software no matter what, because PHP does not constraint or restrict such relationships.
I guess what I'm trying to say is, whatever we decide here does not affect the language - it's the other way around, the language should affect the syntax; and in the case of PHP, there are no constraints when it comes to type-relationships that can (or could be) described a "generic". We would be leveraging an established concept as a means of describing what already exists - we're not adding anything new, nor will we (or can we) change or influence any aspect of the language itself by simply describing it.
In the case of array
, an array is what it is in PHP. It so happens to accept any type of value. If in your program you're putting only one known type of value in your array, you can describe that relationship using generics.
Similarly, you could implement ArrayAccess
and, given that you expect a known type of expected elements T
, you can safely describe the resulting type as array<T>
, since all it is intended to convey is that, when using the []
index operator, you can expect resulting values of a given type.
Bottom line, I don't think we're making any decisions about what array
is or isn't, although we do need to describe the interpretation of generic type-specifications as they relate to the array-type in PHP, but that should be fairly simple, as it would be based on the way arrays are already known to work, e.g.:
array<T>
implies array<int|string,T>
- you didn't specify the index type, and arrays in PHP permit either int
or string
as the index-type.array
implies array<int|string,mixed>
- you didn't specify either index or value type, and arrays accept any type of value.These aren't decisions we're making, it's just how the language works - all we're defining or discussing, is how to best describe the possible constraints. In all cases, those constraints must necessarily be as wide or narrower than those implemented by the language, never wider.
I dunno, is that useful at all? :-)
My name is on the RFC for arrayof, so I am involved in both conversations due to the crossover.— Sent from Mailbox for iPhone
On Sat, Mar 29, 2014 at 6:11 PM, Rasmus Schultz notifications@github.com wrote:
randomly deciding that
array
is a generic class which automatically acts like a generic without the definition sounds f_king disgusting I could understand your drive to take this to internals, if we were talking about a language feature, but we're talking about a type-annotation, which won't be enforced or checked, neither at run-time, nor at compile-time; ideally, it would be checked at design-time (in an IDE, or other tools) but again, it can't be enforced. My point is, type-annotations are advice_, for developers and tools - they are not an extension to the language, but a way to provide static type-hints that can't be expressed by the language itself. You know all this of course, so what I'm getting at is, we're defining a means of describing type-information and type-relationships. These relationships may exist in your software no matter what, because PHP does not constraint or restrict such relationships. I guess what I'm trying to say is, whatever we decide here does not affect the language - it's the other way around, the language should affect the syntax; and in the case of PHP, there are no constraints when it comes to type-relationships that can (or could be) described a "generic". We would be leveraging an established concept as a means of describing what already exists - we're not adding anything new, nor will we (or can we) change or influence any aspect of the language itself by simply describing it. In the case ofarray
, an array is what it is in PHP. It so happens to accept any type of value. If in your program you're putting only one known type of value in your array, you can describe that relationship using generics. Similarly, you could implementArrayAccess
and, given that you expect a known type of expected elementsT
, you can safely describe the resulting type asarray<T>
, since all it is intended to convey is that, when using the[]
index operator, you can expect resulting values of a given type. Bottom line, I don't think we're making any decisions about whatarray
is or isn't, although we do need to describe the interpretation of generic type-specifications as they relate to the array-type in PHP, but that should be fairly simple, as it would be based on the way arrays are already known to work, e.g.:
array<T>
impliesarray<int|string,T>
- you didn't specify the index type, and arrays in PHP permit eitherint
orstring
as the index-type.
array
impliesarray<int|string,mixed>
- you didn't specify either index or value type, and arrays accept any type of value. These aren't decisions we're making, it's just how the language works - all we're defining or discussing, is how to best describe the possible constraints. In all cases, those constraints must necessarily be as wide or narrower than those implemented by the language, never wider. I dunno, is that useful at all? :-)Reply to this email directly or view it on GitHub: https://github.com/phpDocumentor/fig-standards/issues/6#issuecomment-39010765
My name is on the RFC for arrayof, so I am involved in both conversations due to the crossover
I thought arrayof was dead - they voted no, right?
Anyway, arrayof is generics, as has been pointed out by others - and that creates asymmetry, as discussed above... for one, what about reflection? array already does not have a "type" you can reflect on, which is okay, considering array is a value-type and not an object - but with the proposed array types, suddenly you have real types in the mix, and type relationships involving "real" types will occur, which starts to get problematic, as now you have new complex collection types you can't reflect on.
Anyway I think this is off-topic for the discussion at hand (?)
Completely, as I said. -- Phil Sturgeon
On March 30, 2014 at 2:14:26 AM, Rasmus Schultz (notifications@github.com) wrote:
My name is on the RFC for arrayof, so I am involved in both conversations due to the crossover
I thought arrayof was dead - they voted no, right?
Anyway, arrayof is generics, as has been pointed out by others - and that creates asymmetry, as discussed above... for one, what about reflection? array already does not have a "type" you can reflect on, which is okay, considering array is a value-type and not an object - but with the proposed array types, suddenly you have real types in the mix, and type relationships involving "real" types will occur, which starts to get problematic, as now you have new complex collection types you can't reflect on.
Anyway I think this is off-topic for the discussion at hand (?)
— Reply to this email directly or view it on GitHub.
These aren't decisions we're making, it's just how the language works - all we're defining or discussing, is how to best describe the possible constraints. In all cases, those constraints must necessarily be as wide or narrower than those implemented by the language, never wider.
Very well explained indeed. Describing the complex yet implied constraints in a modern PHP application needs documentation standards much more explicit than what the language itself allows.
For what it's worth, my 100% support for this standard.
Sorry if anyone has been confused by my comments in this thread.
The side-by-side conversations of arrayof RFC, generics proposed as a replacement for that RFC and generics as a feature in phpDoc have been conflated.
I am in charge of the arrayof RFC, which is by some considered to be "weak-generics". This really at a basic level just covers "is this an array of stuff", with stuff being a specific type.
If you want full on generic support for docblocks then... well you can't. PHP doesn't do that natively and while you can do it with conventions, I don't think docblocks need to be trying to enforce coding conventions for annotations to end up working properly. That would just be a weird position for us to be in.
PHP will do one of two things. It will either get arrayof in as the existing Foo[] syntax, or it will get generics instead. If I am really lucky it will get both. Or.. well neither. Lol. As has been said this RFC for now has got a no vote, but that doesnt mean it wont ever be in. It DOES mean it wont be in for a while, so something you guys dont need to do is wait for it.
Basically, you should make a decision now, and not wait on PHP internals to answer it for you. No answers will be soon and this PSR can't wait until PHP 5.7 is out.
I have suggested a few things in this thread so I might look like a lunatic, but its a tricky subject especially with the dancing around on internals. My final suggestion; allow "is this an array of stuff" to be documented as Foo[]
and ignore generics entirely as a feature for phpDoc.
Revisit it later, if and when PHP generics look a little more likely.
@philsturgeon I'm still confused by what you said. This proposal has nothing to do with PHP internals, and does not have to wait until the next PHP version is out. It is just a documentation proposal that would allow for tools to better work with existing versions of PHP. Your IDE would be able to understand that this variable is an array of x
, even though PHP doesn't care. It's only about static code inspection doing a better job at understanding what the developer means, and detecting potential mistakes at development time.
My current stance is that we should translate the way hack
has implemented Generics into the PHPDoc Syntax where we also describe that any Object implementing the ArrayAccess
interface or the type array
is automatically interpreted as a Generic that contains elements of the type described between the angular hooks.
Since we need a way to describe what the Concrete Type for a Generic class would be (since we do not have a angular hooks operators for Generics) we need to come up with a tag that can act as a substiture.
@mindplay-dk has thought of an @template
tag but to be frank; I am rather sure this name will lead to confusion as I am not sure that a significant portion of the PHP developers will associate this with Generics or C++ Templates.
My personal suggestion at this moment would be @generic
but I am open to suggestions :)
Doesn't this only apply to the syntax of the datatype position in our @type tag? I had assumed it would just be a matter of the _@type_argument being Foo[].
CRB about.me/ashnazg http://about.me/ashnazg
On Wed, Apr 2, 2014 at 1:49 PM, Mike van Riel notifications@github.comwrote:
My current stance is that we should translate the way hack has implemented Generics into the PHPDoc Syntax where we also describe that any Object implementing the ArrayAccess interface or the type array is automatically interpreted as a Generic that contains elements of the type described between the angular hooks.
Since we need a way to describe what the Concrete Type for a Generic class would be (since we do not have a angular hooks operators for Generics) we need to come up with a tag that can act as a substiture.
@mindplay-dk https://github.com/mindplay-dk has thought of an @templatetag but to be frank; I am rather sure this name will lead to confusion as I am not sure that a significant portion of the PHP developers will associate this with Generics or C++ Templates.
My personal suggestion at this moment would be @generic but I am open to suggestions :)
— Reply to this email directly or view it on GitHubhttps://github.com/phpDocumentor/fig-standards/issues/6#issuecomment-39368626 .
Revisit it later, if and when PHP generics look a little more likely.
I disagree. And I should not here that I, personally, don't even (necessarily) support the idea of adding generics to the language itself, whether "weak" generics as proposed, or full-on generics.
Dynamic languages don't (necessarily) need generics. JavaScript is fine without generics or any type-checks at all. TypeScript is a good (near perfect) example of using generics at compile-time and at design-time (in an IDE) without leaving an unnecessary run-time footprint in the form of type-checking, which is (or can be) just redundant overhead.
Generics as a means of describing type-relationships that may or may not be enforced would be a huge help on larger projects, and I don't think it means we have to wait or bide out time until (or if) PHP itself gets generics. The reason being, generics (as proposed for php-doc) are a means of documenting type-relationships, which currently do exist in code, whether you can formally describe them or not.
Since these relationships do inherently exist, I don't see any reason why we should not be able to document them using generics.
@BenMorel I am talking about comments like this https://github.com/phpDocumentor/fig-standards/issues/6#issuecomment-38999808. There has been a few references to waiting on arrayof or generics or whatever, neither are happening any time soon.
I know what this PSR is all about, I am coordinating it for the FIG. :)
@mindplay-dk: Don't mix up generics and this weak-generic arrayof stuff too much. JavaScript is getting typed arrays, and type hinting against Float32Array
is identical to type hinting against array<float>
(once PHP sorts it scalar type hints out). Anyway, this is not the place for that discussion.
@mvriel:
My current stance is that we should translate the way hack has implemented Generics into the PHPDoc Syntax where we also describe that any Object implementing the ArrayAccess interface or the type array is automatically interpreted as a Generic that contains elements of the type described between the angular hooks.
Fair enough. If you wanna do that then goody for that I guess, but simply using the syntax as "kinda inspiration" for an "actual now-PHP implementation" will probably conflict in the future with a solid generics implementation in the language if one ever does happen. I'd rather not implement something and leave space to fill it in later, than fill it in then have a conflict.
So, IMO it would be better to either leave generics alone until they're added to the language properly (if that ever happens) and for now support arrayof syntax with Foo[], because regardless of arrayof actually going in that is still some logical syntax for the annotation without any downsides.
Anyway, I've clearly said my piece so I'll leave you folks to it.
... and for now support arrayof syntax with Foo[]
My IDE already implements this (it understands @var Foo[]
very well), but I can tell you that it is far from being sufficient.
What it fails specifically on, is understanding traversable objects containing objects:
@var Collection<User>
I have to either declare it as @var Collection
and lose the User
type-hinting on foreach()
, or declare it as User[]
and lose the Collection
type-hinting for any method call.
I don't think this use case is covered by what you suggest, @philsturgeon?
Regardless, I think that restricting us from benefiting from an immediate, huge help during development, is worth taking the risk of an incompatibility with a feature that could very well never happen. Or maybe it will, 8 years from now, and we will have moved on to updated standards as well, and this one will be obsolete anyway.
@BenMorel I understand that Foo[] is already a thing, I want it to be a definite part of this PSR.
I don't think this use case is covered by what you suggest, @philsturgeon?
Right, definitely not. The problem is that we have moved from annotating static code, to enforcing conventions of the PHP that people write to match up with annotations. Sure it could apply to anything with Traversable (not ArrayAccess like some folks have been saying) but that would definitely not be generics.
If you guys want to define an interface for traversable objects with contents of a specific type then by all means, use @traversable or something, but please do not make it into generics as there is no PHP way to define "templates" or anything else about generics until the language offers it to us.
I think we should stop using the words generics or templates or any other non-PHP term as it seems to only bring confusion to this thread.
Although the proposed syntax is borrowed from Java's generics implementation, what is proposed here is not supposed to resemble Java in any other way, and is really just a convenient way of saying "a this
containing a number of that
".
IMHO, this syntax is much clearer to read than yet another @traversable
annotation.
Don't mix up generics and this weak-generic arrayof stuff too much
I view the "weak" generics and arrayof
as specialized cases of generics, at least as far as type-annotations go, since generics can be used to describe both.
please do not make it into generics as there is no PHP way to define "templates" or anything else about generics until the language offers it to us.
That is completely besides the point - the point is, you can describe type-relationships that are already possible in PHP.
You need to stop thinking of generics as a "language feature" - it's just a concept, and as far as annotations, it's just a way to formally describe type-relationships that currently exist and currently cannot be described, except by repeating type-declarations for local vars in for-loops etc. which is not maintainable.
Co-variance is a language feature - where a type-alias (or type-parameter) is used to substitute a real type inside a class or some other piece of code, where run-time checks are performed, etc. If this is ever implemented in PHP, you will get run-time checks only, and not compile-time checks, because PHP is not compiled, and because local vars are untyped, which is another way in which co-variance, the language feature, differs substantially from generics, the concept.
I think we should stop using the words generics or templates or any other non-PHP term as it seems to only bring confusion to this thread
I believe the right term to use is generics. Templates is definitely wrong, as this refers to the C++ code generation feature, something that enables generics in C++ and other languages, but definitely not the same as generics in, say, Java.
I think the best reference cases we have are TypeScript and Dart, because these are (to my knowledge) some of the only dynamic languages that have generics. The best reference case is probably TypeScript, because it doesn't do any run-time checks - the (generic) type-hints are useful (or relevant) only at compile-time and design-time, which is precisely the case in PHP, unless you manually write type-checks using if
-statements, again, precisely the same case in PHP and TypeScript.
I think more of generics as a way of defining a Collection<T>
class.
What we're trying to define here (unless I missed the big point) is just a way of documenting this particular Collection
instance as a "Collection
containing a number of T
", without having to do any specific documentation on the Collection
class itself, and without any other effect outside of this particular instance.
Apart from this terminology thing, I totally agree with you @mindplay-dk.
I would love it if you guys didn't call this feature, the annotation tag or the issue "generics", and used some syntax which did not resemble the Hack generics which might some day make it into PHP.
That would clear up a lot of this confusion.
To me, you guys are definitely talking about a collection and NOT generics.
Even if you are talking about generics, the only type of generics PHP should expect to see is hack-like generics. So again, keep that syntax free.
So again, keep that syntax free.
What do you mean?
I think that restricting us from benefiting from an immediate, huge help during development, is worth taking the risk of an incompatibility with a feature that could very well never happen
I agree.
However, there is no real risk of incompatibility.
Whatever PHP chooses to do (arrayof, generics, etc.) in some distant possible future, it's going to be more narrow (adding constraints) than the current type-constraints, because currently there are no type constraints, because PHP is currently largely ignorant of any static type constraints, the sole exception being argument types. So any implementation of anything resembling generics, by definition, cannot be wider than "no constraints" which is the widest possible, nor is it going to be "different", unless the type-system in PHP as such changes fundamentally, at which point everything else will most likely be obsolete too, but that is even more of a long shot and far more unlikely to happen than generics.
The only possible risk of incompatibility is in terms of literal syntax, and even then, that doesn't necessarily matter - if the syntax in doc-blocks is Foo<Bar>
and the syntax in PHP is something bizarre like Foo{{T:Bar}}
or what have you, it will still work, it'll just look different, again, because generics in type-annotations are just a formal way of describing type-relationships.
Even in the event of something like that happening (in a distant, highly improbable future) you could simply deprecate the generic annotations in doc-blocks entirely, and start using "real" formal generic syntax - no point in repeating information that is already statically available in the actual source-code. Or, you could add new generic annotations with different names. Or you could support both forms of generic syntax for doc-blocks.
There are actually plenty of solutions to this highly unlikely syntax problem.
And even then, it's just a syntax problem.
No reason not to move ahead with generic type-annotations that will enable better tooling and static analysis in IDEs and offline tools now.
Regardless of future development, what you are talking about is not generics. You've said it's not, and you've the suggested the word should not be used to avoid confusion.
Therefore avoiding the syntax is almost certainly a good idea too, as that is going to confuse anyone familiar with hack or the potential PHP feature.
Just use not angle brackets and stop talking about the arrayof RFC and I can get out of this conversation. — Sent from Mailbox for iPhone
On Fri, Apr 4, 2014 at 3:58 AM, Rasmus Schultz notifications@github.com wrote:
I think that restricting us from benefiting from an immediate, huge help during development, is worth taking the risk of an incompatibility with a feature that could very well never happen I agree. However, there is no real risk of incompatibility. Whatever PHP chooses to do (arrayof, generics, etc.) in some distant possible future, it's going to be more narrow (adding constraints) than the current type-constraints, because currently there are no type constraints, because PHP is currently largely ignorant of any static type constraints, the sole exception being argument types. So any implementation of anything resembling generics, by definition, cannot be wider than "no constraints" which is the widest possible, nor is it going to be "different", unless the type-system in PHP as such changes fundamentally, at which point everything else will most likely be obsolete too, but that is even more of a long shot and far more unlikely to happen than generics. The only possible risk of incompatibility is in terms of literal syntax, and even then, that doesn't necessarily matter - if the syntax in doc-blocks is
Foo<Bar>
and the syntax in PHP is something bizarre likeFoo{{T:Bar}}
or what have you, it will still work, it'll just look different, again, because generics in type-annotations are just a formal way of describing type-relationships. Even in the event of something like that happening (in a distant, highly improbable future) you could simply deprecate the generic annotations in doc-blocks entirely, and start using "real" formal generic syntax - no point in repeating information that is already statically available in the actual source-code. Or, you could add new generic annotations with different names. Or you could support both forms of generic syntax for doc-blocks. There are actually plenty of solutions to this highly unlikely syntax problem. And even then, it's just a syntax problem.No reason not to move ahead with generic type-annotations that will enable better tooling and static analysis in IDEs and offline tools now.
Reply to this email directly or view it on GitHub: https://github.com/phpDocumentor/fig-standards/issues/6#issuecomment-39525189
I think that all of you make excellent points despite being in the opposite camps at times. You arguments have convinced me of the following:
As such a syntax should be determined to describe for a Type that it is a Traversable containing types. I will spend time on thinking of various options and I have contacted phpStorm to check whether they already have implemented a variant for this.
At this moment I can think of two variants:
/** @type Collection<stdClass>
or using the constraints syntax (which is still under discussion):
/** @type Collection{stdClass[]}
I will type more on this later when I have thought some more and have received feedback from JetBrains
Good lad. Sounds great.— Sent from Mailbox for iPhone
On Fri, Apr 4, 2014 at 12:06 PM, Mike van Riel notifications@github.com wrote:
I think that all of you make excellent points despite being in the opposite camps at times. You arguments have convinced me of the following:
- A way to document what the type of the key and value of a Traversable object is should be described
- Describing full-fledged Generics is too much of a risk at this point and is overkill (I have yet to receive requests for a pure Generics syntax, only for documenting Traversables)
- Describing Traversables should not be named Generics or Weak Generics syntax to prevent unfulfilled expectations with those who know Generics and to prevent misinformation for those who do not know Generics As such a syntax should be determined to describe for a Type that it is a Traversable containing types. I will spend time on thinking of various options and I have contacted phpStorm to check whether they already have implemented a variant for this. At this moment I can think of two variants:
/** @type Collection<stdClass>
or using the constraints syntax (which is still under discussion):
/** @type Collection{stdClass[]}
I will type more on this later when I have thought some more and have received feedback from JetBrains
Reply to this email directly or view it on GitHub: https://github.com/phpDocumentor/fig-standards/issues/6#issuecomment-39549659
Thank for the nice summary, @mvriel.
I'm personally still in favour of using angle brackets; as long as they're not called generics, I don't think there's a big risk of confusion for people coming from a Java background. After all, many languages share similar syntaxes but with sometimes very different meanings.
That being said, if we're going to explore all the possible options, we could also think of using the square brackets:
Collection[StdClass]
I'm a bit worried of the potential risks of confusion with the []
syntax (already supported in some IDEs) though:
A[]
: traversable object or array of A
objectsA[B]
: A
object, traversable of B
objectsNot sure this is very logical, as both give a different meaning to the type on the left.
@BenMorel thanks for adding that suggestion; I had omitted it on basis of the inconsistency in meaning of the type before the square brackets but you are right: it deserves mentioning (even if it is my personal least favorite ;))
Alright, after discussing this with several people I think the most flexible, concise and forward compatible option would be to support a notation with angular braces. I have opened a new issue (see github note above) and drop this bomb on twitter and on the mailing list to gather input.
I have explicitly opened a new issue to sever all ties with the concept of Generics and to start again with a clean slate.
As such I will close this issue to prevent confusion
Currently it is not possible to define the type of a key and a value contained in a collection object, Generics intends to solve this.
Example:
@param Collection<int> $byte
@param Collection<int,string> $byte
@param Collection<int|string> $byte