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
WIP - Generics #1692
WIP - Generics #1692
Conversation
cc @matej21 |
I hope you don't mind if I just jump in out of the blue with some thoughts as well: Another use case would be Symfony's constraint validators. Each validator implements Dlang templates are an incredibly powerful take on generics, definitely worth a look. Not all of it can possibly be applied to PHP, but still. Regarding interface Type<X> { ... }
function instantiate(Type<Generic> $type): Type<Instantiated> { ... }
// code that only works with one kind of Type:
class Scope {
public function getType(Expr $node): Type<Instantiated> { ... }
...
}
// code that works for both:
class TypeUtils {
public function flattenTypes<X>(Type<X> $type): Type<X>
...
}
... What is Finally, I think leaving out inheritance could be very limiting. Except for callbacks the use cases that come to mind all seem to involve some inheritance or interfaces. Good luck tackling this. PHPstan is a great tool and generics will make it even better! |
As proposed in #1700, a
|
Also worth thinking about Or you could use |
Also if you have any questions/challenges, feel free to ask - https://github.com/vimeo/psalm/blob/master/tests/TemplateTest.php has a whole bunch of tests around this, including for functionality Phan supports. |
/**
* @template T
* @param T::class $entityName
* @return EntityRepository<T>
*/
public function getRepository(string $entityName) {} |
When analysing function/method calls, Psalm first collects templated types from function params in https://github.com/vimeo/psalm/search?q=replaceTemplateTypesWithStandins, then replaces types for the return value: https://github.com/vimeo/psalm/search?q=replaceTemplateTypesWithArgTypes (there's an exception for by-ref values which does the replacement with arg types earlier). The standins (in the above code) are taken from In Psalm there's a direct mapping of class /**
* @template T1
* @template T2
*/
class Foo {
/**
* @param T2 $a
* @param T1 $b
*/
public function __construct($a, $b) {...}
}
That feels intuitively ok to me, but you might feel differently. |
You might want to change the grammar anyway to support class constants in lieu of literal values e.g. https://getpsalm.org/r/079e4222d5 As an extra datapoint, Hack uses |
@JanTvrdik |
@muglug That's awesome! |
This is my most-wanted feature. |
0994dc5
to
fd5902c
Compare
We're working on this with @arnaud-lb - the info here is mostly obsolete. Stay tuned! |
Thinking out loud about generics support which I would like to have as the main feature of 0.12. These are the use cases that come to mind:
Repository<T>::find()
- returns TEntityManager::find(T, id)
- returns T (both are complements for certain types of dynamic return type extensions)array_map
)Here are some write-ups from PHP and TypeScript of how generics are used there:
How to implement them in PHPStan:
EntityRepository<Foo>
in phpDoc and TemplateType represents type placeholder in:@template T
@param T $foo
array<T>
instanceof
s that would break with TemplateType.@template T
indicates a placeholder type identifier and can be bound to a class or to a function/method. It's the only new phpDoc tag that I would add in the first round.Type::resolveTemplateTypes()
.Collection<IntegerType, ObjectType(Article)>
coming from TypeNodeResolver, and in some other places, they will be indexed by their TemplateType identifier. Can we unify this? TypeNodeResolver has to be able to represent invalid data so we can write rules for them. (like havingFoo<A, B>
when there's only one@template
aboveFoo
)functionMap.php
, or even modifying the current file?Additionally, there will have to be some rules for wrong generics usage, like:
Foo<A, B>
when there's only one@template
aboveFoo
(and also vice versa)mixed
?@template T
, then T must occur in constructor parameters. If it's there multiple times, the passed arguments must have the same T.@template T
, then T must occur in its parameters. If it's there multiple times, the passed arguments must have the same T.What I consider out of scope for the first iteration of this feature:
extends Foo<Bar>
. Let's implement this proposed first version, gain benefits and feedback and then decide what should be done.Foo<T extends Bar>
. Again, not needed to gain benefits from generics in the first version.