You have them in theory — you can use generic concepts to model operations — but that dynamism also allows you to break any generic model that might exist.
For example I might say that a function takes a parameter of type "Traversable<string>", but there's nothing stopping me manipulating that object and turning it into a "Traversable<int>" because of that same dynamism.
It might be possible in the future to introduce reified generics which could be guaranteed by the engine, though that may end up being too difficult to add and maintain. In the meantime static analysis tools (like Psalm, which I created) are able to model operations using generics described in docblocks, which works pretty well.
For example I might say that a function takes a parameter of type "Traversable<string>", but there's nothing stopping me manipulating that object and turning it into a "Traversable<int>" because of that same dynamism.
It might be possible in the future to introduce reified generics which could be guaranteed by the engine, though that may end up being too difficult to add and maintain. In the meantime static analysis tools (like Psalm, which I created) are able to model operations using generics described in docblocks, which works pretty well.