Hello internals,
I'd like to introduce an RFC proposing the addition of generic types
and functions:
https://wiki.php.net/rfc/generics
Ben Scholzen started this RFC as a quick draft with a few code samples
in August last year, and I have since then worked with Dominic and
Ben towards a more complete, detailed RFC.
There are a few holes still, which is why it hasn't moved from Draft
to Under Discussion yet, but we feel that it's complete enough that
we can start a discussion about this feature and try to iron out
the remaining details.
The RFC was previously "unofficially" announced on reddit - this
thread generated some good questions and may answer some of the
most immediate questions:
https://www.reddit.com/r/PHP/comments/3zx8qs/php_rfcgenerics_update_03_please_comment/
One of the most common criticisms we've heard, is that the syntax
would be hard to implement, a few have said "impossible" - but we feel
that, if generics are introduced, it's important that the syntax and
features be as familiar as possible to developers who are experienced
with other mainstream web industry languages, such as C# and Java.
To that end, Dominic Grostate has worked through most of the tokenizer/parser
issues - save for one very exotic edge case, his fork demonstrates that the
proposed syntax can be parsed:
https://github.com/orolyn/php-src/commits/generics
Note that this fork is by no means an implementation of generics - it is proof
of concept as far as being able to parse the syntax.
We're hoping to find someone, with more experience working on the php codebase,
who is willing to collaborate on further implementation - and we do also have
a partial test-suite, defining the big picture expectations for most of the
proposed language features:
https://github.com/orolyn/php-src/tree/generics-tests/Zend/tests/generics
We look forward to your comments, questions and (I'm sure) criticisms of
this proposal!
Thank You,
Regards,
Rasmus Schultz
Hello internals,
I'd like to introduce an RFC proposing the addition of generic types
and functions:https://wiki.php.net/rfc/generics
Ben Scholzen started this RFC as a quick draft with a few code samples
in August last year, and I have since then worked with Dominic and
Ben towards a more complete, detailed RFC.There are a few holes still, which is why it hasn't moved from Draft
to Under Discussion yet, but we feel that it's complete enough that
we can start a discussion about this feature and try to iron out
the remaining details.The RFC was previously "unofficially" announced on reddit - this
thread generated some good questions and may answer some of the
most immediate questions:https://www.reddit.com/r/PHP/comments/3zx8qs/php_rfcgenerics_update_03_please_comment/
One of the most common criticisms we've heard, is that the syntax
would be hard to implement, a few have said "impossible" - but we feel
that, if generics are introduced, it's important that the syntax and
features be as familiar as possible to developers who are experienced
with other mainstream web industry languages, such as C# and Java.To that end, Dominic Grostate has worked through most of the tokenizer/parser
issues - save for one very exotic edge case, his fork demonstrates that the
proposed syntax can be parsed:https://github.com/orolyn/php-src/commits/generics
Note that this fork is by no means an implementation of generics - it is proof
of concept as far as being able to parse the syntax.We're hoping to find someone, with more experience working on the php codebase,
who is willing to collaborate on further implementation - and we do also have
a partial test-suite, defining the big picture expectations for most of the
proposed language features:https://github.com/orolyn/php-src/tree/generics-tests/Zend/tests/generics
We look forward to your comments, questions and (I'm sure) criticisms of
this proposal!Thank You,
Regards,
Rasmus Schultz--
Nice work.
I think you there's a typo in one of your examples:
write(new Entry<int, int>(1, 2)); // throws a TypeError
Presumably this should be <int, string> or something similar?
Hello internals,
I'd like to introduce an RFC proposing the addition of generic types
and functions:https://wiki.php.net/rfc/generics
Ben Scholzen started this RFC as a quick draft with a few code samples
in August last year, and I have since then worked with Dominic and
Ben towards a more complete, detailed RFC.There are a few holes still, which is why it hasn't moved from Draft
to Under Discussion yet, but we feel that it's complete enough that
we can start a discussion about this feature and try to iron out
the remaining details.The RFC was previously "unofficially" announced on reddit - this
thread generated some good questions and may answer some of the
most immediate questions:https://www.reddit.com/r/PHP/comments/3zx8qs/php_rfcgenerics_update_03_please_comment/
One of the most common criticisms we've heard, is that the syntax
would be hard to implement, a few have said "impossible" - but we feel
that, if generics are introduced, it's important that the syntax and
features be as familiar as possible to developers who are experienced
with other mainstream web industry languages, such as C# and Java.To that end, Dominic Grostate has worked through most of the tokenizer/parser
issues - save for one very exotic edge case, his fork demonstrates that the
proposed syntax can be parsed:https://github.com/orolyn/php-src/commits/generics
Note that this fork is by no means an implementation of generics - it is proof
of concept as far as being able to parse the syntax.We're hoping to find someone, with more experience working on the php codebase,
who is willing to collaborate on further implementation - and we do also have
a partial test-suite, defining the big picture expectations for most of the
proposed language features:https://github.com/orolyn/php-src/tree/generics-tests/Zend/tests/generics
We look forward to your comments, questions and (I'm sure) criticisms of
this proposal!Thank You,
Regards,
Rasmus Schultz--
Nice work.
I think you there's a typo in one of your examples:
write(new Entry<int, int>(1, 2)); // throws a TypeError
Presumably this should be <int, string> or something similar?
Ignore me, I didn't read the example properly.
We would really like some feedback on this. We know it is a massive
feature, but we have put in quite a lot of research in both the language
implications and implementation challenges. Please ask any questions you
have.
Thanks.
On Mon, Apr 18, 2016 at 10:47 AM, Josh Di Fabio joshdifabio@gmail.com
wrote:On Mon, Apr 18, 2016 at 6:20 AM, Rasmus Schultz rasmus@mindplay.dk
wrote:Hello internals,
I'd like to introduce an RFC proposing the addition of generic types
and functions:https://wiki.php.net/rfc/generics
Ben Scholzen started this RFC as a quick draft with a few code samples
in August last year, and I have since then worked with Dominic and
Ben towards a more complete, detailed RFC.There are a few holes still, which is why it hasn't moved from Draft
to Under Discussion yet, but we feel that it's complete enough that
we can start a discussion about this feature and try to iron out
the remaining details.The RFC was previously "unofficially" announced on reddit - this
thread generated some good questions and may answer some of the
most immediate questions:https://www.reddit.com/r/PHP/comments/3zx8qs/php_rfcgenerics_update_03_please_comment/
One of the most common criticisms we've heard, is that the syntax
would be hard to implement, a few have said "impossible" - but we feel
that, if generics are introduced, it's important that the syntax and
features be as familiar as possible to developers who are experienced
with other mainstream web industry languages, such as C# and Java.To that end, Dominic Grostate has worked through most of the
tokenizer/parser
issues - save for one very exotic edge case, his fork demonstrates that
the
proposed syntax can be parsed:https://github.com/orolyn/php-src/commits/generics
Note that this fork is by no means an implementation of generics - it
is proof
of concept as far as being able to parse the syntax.We're hoping to find someone, with more experience working on the php
codebase,
who is willing to collaborate on further implementation - and we do
also have
a partial test-suite, defining the big picture expectations for most of
the
proposed language features:https://github.com/orolyn/php-src/tree/generics-tests/Zend/tests/generics
We look forward to your comments, questions and (I'm sure) criticisms of
this proposal!Thank You,
Regards,
Rasmus Schultz--
Nice work.
I think you there's a typo in one of your examples:
write(new Entry<int, int>(1, 2)); // throws a TypeError
Presumably this should be <int, string> or something similar?
Ignore me, I didn't read the example properly.
We would really like some feedback on this. We know it is a massive
feature, but we have put in quite a lot of research in both the language
implications and implementation challenges. Please ask any questions you
have.
thank you all for your work on this. That's a very interesting feature !
I guess it is possible but I have not seen it explicitly, can I do this:
class StringKeyEntry<T> extends Entry<string,T>
{}
In "Traits" there is mistake;
class Box<T>
{
use Box<T>;
}
should be
class Box<T>
{
use Container<T>;
}
about the upper bounds, have you consider another way of describing the
constraints, eg:
class Box<T> where T is Boxable
{
// ...
}
this would allow multiple constraints, eg:
class Collection<T> where T is Traversable, T is Countable
{
// ...
}
and, maybe, union type
class Collection<T> where T is one of (\first\lib\Collection, \seconf\lib\Arrayable)
{
// ...
}
can generic types be nested ?
eg:
class Stuff<A, B is Something<A, string>>
{
// ...
}
considering the related enhancements, I guess this rfc is already big
and both are useful enough to be proposed on their own.
regards,
Thanks.
On Mon, Apr 18, 2016 at 10:47 AM, Josh Di Fabio joshdifabio@gmail.com
wrote:On Mon, Apr 18, 2016 at 6:20 AM, Rasmus Schultz rasmus@mindplay.dk
wrote:Hello internals,
I'd like to introduce an RFC proposing the addition of generic types
and functions:https://wiki.php.net/rfc/generics
Ben Scholzen started this RFC as a quick draft with a few code samples
in August last year, and I have since then worked with Dominic and
Ben towards a more complete, detailed RFC.There are a few holes still, which is why it hasn't moved from Draft
to Under Discussion yet, but we feel that it's complete enough that
we can start a discussion about this feature and try to iron out
the remaining details.The RFC was previously "unofficially" announced on reddit - this
thread generated some good questions and may answer some of the
most immediate questions:https://www.reddit.com/r/PHP/comments/3zx8qs/php_rfcgenerics_update_03_please_comment/
One of the most common criticisms we've heard, is that the syntax
would be hard to implement, a few have said "impossible" - but we feel
that, if generics are introduced, it's important that the syntax and
features be as familiar as possible to developers who are experienced
with other mainstream web industry languages, such as C# and Java.To that end, Dominic Grostate has worked through most of the
tokenizer/parser
issues - save for one very exotic edge case, his fork demonstrates that
the
proposed syntax can be parsed:https://github.com/orolyn/php-src/commits/generics
Note that this fork is by no means an implementation of generics - it
is proof
of concept as far as being able to parse the syntax.We're hoping to find someone, with more experience working on the php
codebase,
who is willing to collaborate on further implementation - and we do
also have
a partial test-suite, defining the big picture expectations for most of
the
proposed language features:https://github.com/orolyn/php-src/tree/generics-tests/Zend/tests/generics
We look forward to your comments, questions and (I'm sure) criticisms of
this proposal!Thank You,
Regards,
Rasmus Schultz--
Nice work.
I think you there's a typo in one of your examples:
write(new Entry<int, int>(1, 2)); // throws a TypeError
Presumably this should be <int, string> or something similar?
Ignore me, I didn't read the example properly.
--
Mathieu Rochette
about the upper bounds, have you consider another way of describing the
constraints, eg:class Box<T> where T is Boxable
this would allow multiple constraints, eg:
class Collection<T> where T is Traversable, T is Countable
IMO, this sort of problem should be solved by combining this feature
with union types, so you could have something like:
class Collection<T as (Traversable | Countable)> {...
And merely inherit the logic rules from that feature rather than
inventing yet another one.
can generic types be nested ?
class Stuff<A, B is Something<A, string>>
I can't imagine why not...
For my part, I love the concept overall. Generics are an important
part of moving PHP towards comprehensive type-safety. But then, you
know how I feel about Hack. :)
-Sara
Hi!
class Collection<T as (Traversable | Countable)> {...
I am sorry if this sounds harsh, but I really hope we won't have
something like this in PHP. Java templates are complex and weird enough,
adding another layer on top of that to allow type expressions IMHO is
really taking it too far.
--
Stas Malyshev
smalyshev@gmail.com
class Collection<T as (Traversable | Countable)> {...
I am sorry if this sounds harsh, but I really hope we won't have
something like this in PHP. Java templates are complex and weird enough,
adding another layer on top of that to allow type expressions IMHO is
really taking it too far.
Nah, I've yet to actually have use for anything so specific yet
either, but in the context of Mathieu was asking for, it seemed like a
good moment to direct the concept back to something more consistent,
at least.
-Sara
Hi,
Here are a couple of comments towards Generics support to PHP.
1- Even though mentioned, I'd still use "extends" or "implements" instead
of "is" (which would be a new pseudo-reserved keyword) to enforce data type
consistency and prevent developers to potentially referring to one thing
while consider another.
2- I'd avoid blindly inferring generic type using constructor without
notifying the user about this being a generic class. I'd still prefer a
diamond operator <> to be used. This would be specially valid if we ever
decide to merge typed properties too. Example:
class Foo {
private Bar<string> bar;
public function __construct() {
$this->bar = new Bar<>("Hello World"); // Can be omitted because of
typed properties already defined it
}
}
3- I didn't see any description related to nested generic type definition.
I hope it is supported. Example:
$pair = new Tuple<string, Map<string, Foo>>();
4- Another thing not mentioned in the RFC is if constructor also support
generic typing, like this:
class Foo<A> {
public function __construct<B>(B $b) {}
}
$foo = new Foo<string>(1);
5- Java provides an enforcement that you can specify multiple bounds.
Imagine a class that contains a generic that that must extend class A, but
also implement interfaces B and C. We'd enter in a language recognition
problem if we take the same approach as class declaration as definition,
such as: class Foo<T extends A implements B, C>
In order to properly solve this, Java takes the approach of &, and require
the class to be first and interfaces later, like this: class Foo<T extends
A & B & C>. As I already mentioned in comment #1, we should stick to
extends and implements, but adopt & as multiple interface implementation,
like this:
class Foo<T extends A implements B & C> {}
6- This could be an independent RFC, but ideally we should support generic
typed arrays using the [] syntax, like this:
function sortElements<E extends Element>(E[] $elements) : E[];
7- Typed returns are also not mentioned in RFC:
function newEntity<E implements Entity>($id) : E;
$user = newEntity<User>(UUID::v4());
8- RFC only handles upper bound generic types, but no support is mentioned
about lower bound generic types.
An upper bound generic type is when it restricts the unknown type to that
type or one of its inherited types. In RFC this is specified by the "is"
keyword and I suggested to still segregate it to "extends" as highlighted
in comment #1. A lower bound generic type is when it restrict the unknown
type to that type or any of the is parent types. I'd suggest to use
"parent", "parents", "parentof" or "super". Example:
class A {}
class B extends A {}
class C extends A {}
class D extends B {}
class Foo<T parent B> {} // T can be an instance of A or B but never C or D.
9- Although not clearly clarified in RFC, I saw some inference over
unbounded wildcard types. However, nothing is mentioned about wildcard
upper bound and wildcard lower bounds. A few examples:
// Unbounded wildcard type
function sort(List $list) : void;
// ... how Java (here, PHPized) does it:
function sort(List<?> list) : void;
// Wildcard upper bound in Java (PHPized)
function sortElements(List<? extends Element> $list) : void;
// Wildcard lower bound in Java (PHPized)
function addAll(List<? super Integer> $list) : void;
There're problems of using the unbounded wildcard generic type, but I think
you may have to dig through Java language specification to better
understand what I'm saying. You can poke me privately and I can point out
documentation that exposes its limitations/constraints.
10- Reflection plays a very important piece in this support and IMHO it
must be documented with proposed API for verification. A link to a gist is
not enough to fully understand what is proposed, so I'm abstaining from
reviewing it until it gets properly mapped out in RFC.
I hope I gave enough insight of what I consider that needs to be expanded
in RFC, expanded in the proposal, highlighted better, etc. Still, this RFC
is the best attempt I've seen towards improving the reusability of data
structures in PHP. Nice work! =)
Regards,
On Tue, Apr 19, 2016 at 4:13 PM, Stanislav Malyshev smalyshev@gmail.com
wrote:class Collection<T as (Traversable | Countable)> {...
I am sorry if this sounds harsh, but I really hope we won't have
something like this in PHP. Java templates are complex and weird enough,
adding another layer on top of that to allow type expressions IMHO is
really taking it too far.Nah, I've yet to actually have use for anything so specific yet
either, but in the context of Mathieu was asking for, it seemed like a
good moment to direct the concept back to something more consistent,
at least.-Sara
--
--
Guilherme Blanco
Lead Architect at E-Block
On Tue, Apr 19, 2016 at 7:54 PM, guilhermeblanco@gmail.com
guilhermeblanco@gmail.com wrote:
1- Even though mentioned, I'd still use "extends" or "implements" instead of
"is" (which would be a new pseudo-reserved keyword) to enforce data type
consistency and prevent developers to potentially referring to one thing
while consider another.
FWIW, This is why Hack chose "as", which is already a special keyword
and is much more concise than extends/implements.
-Sara
guilhermeblanco@gmail.com wrote on 20/04/2016 03:54:
1- Even though mentioned, I'd still use "extends" or "implements" instead
of "is" (which would be a new pseudo-reserved keyword) to enforce data type
consistency and prevent developers to potentially referring to one thing
while consider another.
Perhaps "instanceof" would make more sense here? "class FileProcessor<T
extends FileHandle>" seems to exclude instantiating
FileProcessor<FileHandle>, because "FileHandle extends FileHandle"
doesn't make any sense.
You could read "class Box<T instanceof Boxable>" as an assertion that
the class passes the constraint, as in "public function __construct(T
$t) { assert($t instanceof Boxable); }"
Regards,
Rowan Collins
[IMSoP]
guilhermeblanco@gmail.com wrote on 20/04/2016 03:54:
1- Even though mentioned, I'd still use "extends" or "implements"
instead
of "is" (which would be a new pseudo-reserved keyword) to enforce
data type
consistency and prevent developers to potentially referring to one thing
while consider another.Perhaps "instanceof" would make more sense here? "class
FileProcessor<T extends FileHandle>" seems to exclude instantiating
FileProcessor<FileHandle>, because "FileHandle extends FileHandle"
doesn't make any sense.You could read "class Box<T instanceof Boxable>" as an assertion that
the class passes the constraint, as in "public function __construct(T
$t) { assert($t instanceof Boxable); }"Regards,
I'm not an expert in generics by any means, but how is that different
than just
public function __construct(Boxable $t) {}
I thought the point of generics was for cases like "these two variables
must be the same type, but they can be any same type."
--
--Larry Garfield
Larry Garfield wrote on 20/04/2016 16:12:
guilhermeblanco@gmail.com wrote on 20/04/2016 03:54:
1- Even though mentioned, I'd still use "extends" or "implements"
instead
of "is" (which would be a new pseudo-reserved keyword) to enforce
data type
consistency and prevent developers to potentially referring to one
thing
while consider another.Perhaps "instanceof" would make more sense here? "class
FileProcessor<T extends FileHandle>" seems to exclude instantiating
FileProcessor<FileHandle>, because "FileHandle extends FileHandle"
doesn't make any sense.You could read "class Box<T instanceof Boxable>" as an assertion that
the class passes the constraint, as in "public function __construct(T
$t) { assert($t instanceof Boxable); }"Regards,
I'm not an expert in generics by any means, but how is that different
than justpublic function __construct(Boxable $t) {}
I thought the point of generics was for cases like "these two
variables must be the same type, but they can be any same type."
The is/extends/instanceof clause adds to the rule: "...they can be any
same type, as long as that type meets this constraint"
So using an unbounded generic, you could manually check:
class FileProcessor<T>
{
public function __construct(T $t) {
assert($t instanceof FileHandle);
}
}
$f = new FileProcessor<ZipArchiveEntryHandle>($zip->getEntryHandle(1));
// "T" is substituted for "ZipArchiveEntryHandle"
$f = new FileProcessor<int>(42); // assertion fails!
A bounded generic, using "is" as the keyword as in the current RFC,
performs the assertion for you:
class FileProcessor<T is FileHandle>
{
public function __construct(T $t) {
// no need for an assertion
}
}
$f = new FileProcessor<int>(42); // throws a TypeError
My suggestion was that the constraint is like an "instanceof" assertion,
so we could use that keyword:
class FileProcessor<T instanceof FileHandle>
{
public function __construct(T $t) {
// no need for an assertion
}
}
$f = new FileProcessor<int>(42); // throws a TypeError
I hope that makes sense.
Regards,
Rowan Collins
[IMSoP]
Yes, if you have "class Box<T extends Boxable>" and T is used for two
things, then "they must be of the same type, but they can be any same type
and that type must at least be Boxable". It means that Box can use a T as a
Boxable (call Boxable methods on it, etc).
On Thu, Apr 21, 2016 at 1:12 AM, Larry Garfield larry@garfieldtech.com
wrote:
guilhermeblanco@gmail.com wrote on 20/04/2016 03:54:
1- Even though mentioned, I'd still use "extends" or "implements" instead
of "is" (which would be a new pseudo-reserved keyword) to enforce data
type
consistency and prevent developers to potentially referring to one thing
while consider another.Perhaps "instanceof" would make more sense here? "class FileProcessor<T extends FileHandle>" seems to exclude instantiating
FileProcessor<FileHandle>, because "FileHandle extends FileHandle" doesn't
make any sense.You could read "class Box<T instanceof Boxable>" as an assertion that the
class passes the constraint, as in "public function __construct(T $t) {
assert($t instanceof Boxable); }"Regards,
I'm not an expert in generics by any means, but how is that different than
justpublic function __construct(Boxable $t) {}
I thought the point of generics was for cases like "these two variables
must be the same type, but they can be any same type."--
--Larry Garfield
Sure, the usage of "instanceof" is an alternative to prevent declaring a
new keyword. PHP doesn't have the same problem as Java does to enforce the
first generic type information to be a class and others interfaces. So in
this:
class A {}
interface B {}
interface C {}
class Foo<T instanceof A & B & C> {}
Java required A to be the first due to its compilation process, which PHP
does not have. PHP would accept any order of A, B and C there, like "C & B
&A".
PS: I like to use Java as a base for Generics because I feel it more well
thought than other language implementations (like .NET and Swift).
Regards,
On Wed, Apr 20, 2016 at 11:01 AM, Rowan Collins rowan.collins@gmail.com
wrote:
guilhermeblanco@gmail.com wrote on 20/04/2016 03:54:
1- Even though mentioned, I'd still use "extends" or "implements" instead
of "is" (which would be a new pseudo-reserved keyword) to enforce data
type
consistency and prevent developers to potentially referring to one thing
while consider another.Perhaps "instanceof" would make more sense here? "class FileProcessor<T extends FileHandle>" seems to exclude instantiating
FileProcessor<FileHandle>, because "FileHandle extends FileHandle" doesn't
make any sense.You could read "class Box<T instanceof Boxable>" as an assertion that the
class passes the constraint, as in "public function __construct(T $t) {
assert($t instanceof Boxable); }"Regards,
Rowan Collins
[IMSoP]--
--
Guilherme Blanco
Lead Architect at E-Block
On Tue, Apr 19, 2016 at 1:16 PM, Mathieu Rochette mathieu@texthtml.net
wrote:about the upper bounds, have you consider another way of describing the
constraints, eg:class Box<T> where T is Boxable
this would allow multiple constraints, eg:
class Collection<T> where T is Traversable, T is Countable
IMO, this sort of problem should be solved by combining this feature
with union types, so you could have something like:class Collection<T as (Traversable | Countable)> {...
And merely inherit the logic rules from that feature rather than
inventing yet another one.
I think the "where T is Traversable, T is Countable" syntax was intended to
represent the intersection (i.e. a "Countable Traversable"), not the union.
So you would need an intersection syntax
class Collection<T as Traversable & Countable> {...
can generic types be nested ?
class Stuff<A, B is Something<A, string>>
I can't imagine why not...
For my part, I love the concept overall. Generics are an important
part of moving PHP towards comprehensive type-safety. But then, you
know how I feel about Hack. :)-Sara
class Collection<T as (Traversable | Countable)> {...
I think the "where T is Traversable, T is Countable" syntax was intended to
represent the intersection (i.e. a "Countable Traversable"), not the union.So you would need an intersection syntax
class Collection<T as Traversable & Countable> {...
And that's precisely my point for why reusing the suggested syntax for
union types makes sense. You can to specify either intersection or
union (or potentially a complex combination of the two (A | (B & C))),
rather than being limited to intersections only.
-Sara
I agree.
On Tue, Apr 19, 2016 at 7:56 PM, Jesse Schalken me@jesseschalken.com
wrote:class Collection<T as (Traversable | Countable)> {...
I think the "where T is Traversable, T is Countable" syntax was intended
to
represent the intersection (i.e. a "Countable Traversable"), not the
union.So you would need an intersection syntax
class Collection<T as Traversable & Countable> {...
And that's precisely my point for why reusing the suggested syntax for
union types makes sense. You can to specify either intersection or
union (or potentially a complex combination of the two (A | (B & C))),
rather than being limited to intersections only.-Sara
about the upper bounds, have you consider another way of describing the
constraints, eg:class Box<T> where T is Boxable
this would allow multiple constraints, eg:
class Collection<T> where T is Traversable, T is Countable
IMO, this sort of problem should be solved by combining this feature
with union types, so you could have something like:class Collection<T as (Traversable | Countable)> {...
And merely inherit the logic rules from that feature rather than
inventing yet another one.
obviously if the union type rfc passes we don't need another way of
expressing this.
that was only in the case it does not, I think having a way to have at
least types intersection
is useful here (and I didn't event think about <T is A & B>)can generic types be nested ?
class Stuff<A, B is Something<A, string>>
I can't imagine why not...
just to be clear, it's not just nested generic. the A type have to be
same in both "subtypes"For my part, I love the concept overall. Generics are an important
part of moving PHP towards comprehensive type-safety. But then, you
know how I feel about Hack. :)-Sara
I've made an amendment to the RFC to clarify on the Nested Types, which is
indeed supposed to be part of the feature. Rasmus may want to reword it if
it isn't very clear.
Regarding union and intersections for upper (and maybe lower) bounds.
Would it be appropriate to exclude these from type parameters until their
respective RFCs are approved? As including them in generics but not in
standard type hints may create an inconsistency.
In short, perhaps a generics implementation should incorporate unions (and
any future type constraints) as existing features only. This would help
RFC Generics to focus on: Type aliasing, Introspection and Reflection.
On Tue, Apr 19, 2016 at 1:16 PM, Mathieu Rochette mathieu@texthtml.net
wrote:about the upper bounds, have you consider another way of describing the
constraints, eg:class Box<T> where T is Boxable
this would allow multiple constraints, eg:
class Collection<T> where T is Traversable, T is Countable
IMO, this sort of problem should be solved by combining this feature
with union types, so you could have something like:class Collection<T as (Traversable | Countable)> {...
And merely inherit the logic rules from that feature rather than
inventing yet another one.obviously if the union type rfc passes we don't need another way of
expressing this.
that was only in the case it does not, I think having a way to have at
least types intersection
is useful here (and I didn't event think about <T is A & B>)can generic types be nested ?
class Stuff<A, B is Something<A, string>>
I can't imagine why not...
just to be clear, it's not just nested generic. the A type have to be same
in both "subtypes"For my part, I love the concept overall. Generics are an important
part of moving PHP towards comprehensive type-safety. But then, you
know how I feel about Hack. :)-Sara
On Wed, Apr 20, 2016 at 11:10 AM, Dominic Grostate <
codekestrel@googlemail.com> wrote:
I've made an amendment to the RFC to clarify on the Nested Types, which is
indeed supposed to be part of the feature. Rasmus may want to reword it if
it isn't very clear.Regarding union and intersections for upper (and maybe lower) bounds.
Would it be appropriate to exclude these from type parameters until their
respective RFCs are approved? As including them in generics but not in
standard type hints may create an inconsistency.In short, perhaps a generics implementation should incorporate unions (and
any future type constraints) as existing features only. This would help
RFC Generics to focus on: Type aliasing, Introspection and Reflection.
That would be a wise move, there are currently some RFCs with similar areas
of contention
On Tue, Apr 19, 2016 at 1:16 PM, Mathieu Rochette <mathieu@texthtml.net
wrote:
about the upper bounds, have you consider another way of describing the
constraints, eg:class Box<T> where T is Boxable
this would allow multiple constraints, eg:
class Collection<T> where T is Traversable, T is Countable
IMO, this sort of problem should be solved by combining this feature
with union types, so you could have something like:class Collection<T as (Traversable | Countable)> {...
And merely inherit the logic rules from that feature rather than
inventing yet another one.obviously if the union type rfc passes we don't need another way of
expressing this.
that was only in the case it does not, I think having a way to have at
least types intersection
is useful here (and I didn't event think about <T is A & B>)can generic types be nested ?
class Stuff<A, B is Something<A, string>>
I can't imagine why not...
just to be clear, it's not just nested generic. the A type have to be
same
in both "subtypes"For my part, I love the concept overall. Generics are an important
part of moving PHP towards comprehensive type-safety. But then, you
know how I feel about Hack. :)-Sara
I don't know if mid-thread answering may lead to top-posting, but if it
does, I'm sorry... =\
Answer inline:
On Wed, Apr 20, 2016 at 5:10 AM, Dominic Grostate <
codekestrel@googlemail.com> wrote:
I've made an amendment to the RFC to clarify on the Nested Types, which is
indeed supposed to be part of the feature. Rasmus may want to reword it if
it isn't very clear.Regarding union and intersections for upper (and maybe lower) bounds.
Would it be appropriate to exclude these from type parameters until their
respective RFCs are approved? As including them in generics but not in
standard type hints may create an inconsistency.In short, perhaps a generics implementation should incorporate unions (and
any future type constraints) as existing features only. This would help
RFC Generics to focus on: Type aliasing, Introspection and Reflection.
Unions and Intersections are required anyway, and I don't see how you can
implement generics without supporting them.
Let's say I'm implementing a cache library that segregate the interface of
BulkOperations from the basic operations named CacheDriver.
In a given class, I might want to only accept CacheDrivers that also
support BulkOperations. How would I achieve that?
The same happens to upper bounds that someone asked for an example
privately.
If I want to hire/move a person to a department that is registered in the
system, but is not a 3rd party company person, how would you achieve that
considering the following class structure?
class Person {}
class Employee extends Person {}
class AssociateEmployee extends Employee {}
class Manager extends Employee {}
Considering your function:
function assignToDepartment<T>(T $person) : bool;
Generic type "T" in the function prototype needs to accept Person (new
hire), Employee and Manager (transfer), but not AssociateEmployee.
Considering upper bounds support only, your best bet is "T extends Person",
but that would accept AssociateEmployee to be provided, which contradicts
the business rule. Accepting anything lower in the hierarchy prevents new
hires.
That's when lower bounds comes into play. If you define as "T super Manager",
all Person, Employee and Manager gets accepted, but not the
AssociatedEmployee, matching properly the business rule.
Also, someone else asked about type inference over my comment #4 in this
example:
class Foo<A> {
public function __construct<B>(B $b) {}
}
$foo = new Foo<string>(1);
The question asked is if that "string" was an inference over A or B. The
correct answer is A, because generic type B is inferred from the parameter
(integer) provided. That is exactly why class generic type definition
should never be inferred from the constructor arguments, because you make
it impossible to support both constructor generic types AND class generic
type definitions at the same time.
On Tue, Apr 19, 2016 at 1:16 PM, Mathieu Rochette <mathieu@texthtml.net
wrote:
about the upper bounds, have you consider another way of describing the
constraints, eg:class Box<T> where T is Boxable
this would allow multiple constraints, eg:
class Collection<T> where T is Traversable, T is Countable
IMO, this sort of problem should be solved by combining this feature
with union types, so you could have something like:class Collection<T as (Traversable | Countable)> {...
And merely inherit the logic rules from that feature rather than
inventing yet another one.obviously if the union type rfc passes we don't need another way of
expressing this.
that was only in the case it does not, I think having a way to have at
least types intersection
is useful here (and I didn't event think about <T is A & B>)can generic types be nested ?
class Stuff<A, B is Something<A, string>>
I can't imagine why not...
just to be clear, it's not just nested generic. the A type have to be
same
in both "subtypes"For my part, I love the concept overall. Generics are an important
part of moving PHP towards comprehensive type-safety. But then, you
know how I feel about Hack. :)-Sara
--
Guilherme Blanco
Lead Architect at E-Block
guilhermeblanco@gmail.com wrote on 20/04/2016 15:44:
In a given class, I might want to only accept CacheDrivers that also
support BulkOperations. How would I achieve that?
Apologies if this has been refuted elsewhere, but AFAIK you can
implement any intersection constraint, albeit slightly more verbosely,
by creating a new type:
- You can create a new interface that expresses an intersection of two
other interfaces:
interface BulkOperationCacheDriver extends CacheDriver, BulkOperation {}
- Similarly, the intersection of a class and an interface can be created
as an abstract class:
abstact class BulkRedisCacheDriver extends RedisCacheDriver implements
BulkOperation {}
Since you can create those without modifying the underlying classes, you
could specify generic constraints for any given intersection. A union
constraint ("it can be either this or this") cannot currently be
implemented, but the use cases are somewhat more questionable anyway.
Regards,
Rowan Collins
[IMSoP]
I agree on both points (technically). It would allow you to apply that
restriction. I only advise against it to reduce the impact the initial
implementation would have on the codebase, provided it is preferable to
implement it in phases.
As for inference. Rasmus and I have argued over that a fair bit, as I was
in favour of using 'new Entry<>' or just 'new Entry' to be a parameterised
type with no type constraints (any value).
On 20 Apr 2016 3:44 p.m., "guilhermeblanco@gmail.com" <
guilhermeblanco@gmail.com> wrote:
I don't know if mid-thread answering may lead to top-posting, but if it
does, I'm sorry... =\Answer inline:
On Wed, Apr 20, 2016 at 5:10 AM, Dominic Grostate <
codekestrel@googlemail.com> wrote:I've made an amendment to the RFC to clarify on the Nested Types, which is
indeed supposed to be part of the feature. Rasmus may want to reword it
if
it isn't very clear.Regarding union and intersections for upper (and maybe lower) bounds.
Would it be appropriate to exclude these from type parameters until their
respective RFCs are approved? As including them in generics but not in
standard type hints may create an inconsistency.In short, perhaps a generics implementation should incorporate unions (and
any future type constraints) as existing features only. This would help
RFC Generics to focus on: Type aliasing, Introspection and Reflection.Unions and Intersections are required anyway, and I don't see how you can
implement generics without supporting them.
Let's say I'm implementing a cache library that segregate the interface of
BulkOperations from the basic operations named CacheDriver.
In a given class, I might want to only accept CacheDrivers that also
support BulkOperations. How would I achieve that?The same happens to upper bounds that someone asked for an example
privately.
If I want to hire/move a person to a department that is registered in the
system, but is not a 3rd party company person, how would you achieve that
considering the following class structure?class Person {}
class Employee extends Person {}
class AssociateEmployee extends Employee {}
class Manager extends Employee {}Considering your function:
function assignToDepartment<T>(T $person) : bool;
Generic type "T" in the function prototype needs to accept Person (new
hire), Employee and Manager (transfer), but not AssociateEmployee.
Considering upper bounds support only, your best bet is "T extends Person",
but that would accept AssociateEmployee to be provided, which contradicts
the business rule. Accepting anything lower in the hierarchy prevents new
hires.
That's when lower bounds comes into play. If you define as "T super
Manager", all Person, Employee and Manager gets accepted, but not the
AssociatedEmployee, matching properly the business rule.Also, someone else asked about type inference over my comment #4 in this
example:class Foo<A> {
public function __construct<B>(B $b) {}
}$foo = new Foo<string>(1);
The question asked is if that "string" was an inference over A or B. The
correct answer is A, because generic type B is inferred from the parameter
(integer) provided. That is exactly why class generic type definition
should never be inferred from the constructor arguments, because you make
it impossible to support both constructor generic types AND class generic
type definitions at the same time.On Tue, Apr 19, 2016 at 1:16 PM, Mathieu Rochette <
mathieu@texthtml.net>
wrote:about the upper bounds, have you consider another way of describing
the
constraints, eg:class Box<T> where T is Boxable
this would allow multiple constraints, eg:
class Collection<T> where T is Traversable, T is Countable
IMO, this sort of problem should be solved by combining this feature
with union types, so you could have something like:class Collection<T as (Traversable | Countable)> {...
And merely inherit the logic rules from that feature rather than
inventing yet another one.obviously if the union type rfc passes we don't need another way of
expressing this.
that was only in the case it does not, I think having a way to have at
least types intersection
is useful here (and I didn't event think about <T is A & B>)can generic types be nested ?
class Stuff<A, B is Something<A, string>>
I can't imagine why not...
just to be clear, it's not just nested generic. the A type have to be
same
in both "subtypes"For my part, I love the concept overall. Generics are an important
part of moving PHP towards comprehensive type-safety. But then, you
know how I feel about Hack. :)-Sara
--
Guilherme Blanco
Lead Architect at E-Block
Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or multiple
there of.
The preferred keywords appear to be either "as" or "instanceof".
class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}
We would like to know for sure then if everyone is largely against the
addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are approved,
then generics would have to support them as well. Likewise if this feature
becomes approved in generics, it would make sense to support them in
regular type hints as well.
The RFC makes a reference to generic closures, which may look something
like this:
function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature at
https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each other
(please correct me if I am wrong).
My question about this is would you prefer the generics RFC exclude this
part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>". However to
ease the implementation it was decided that should be a separate feature.
So we'd like to find out if everyone else feels the same way about callable
types.
This RFC currently doesn't specify in detail how reflection would work. We
have attempted a few API designs, but due to generic classes being ...
generic, it is difficult to find a suitable way to glean information about
a class in a backwards compatible manner. So we will need some help on
this one.
Aside from these top issues on our own list, however does everyone feel
about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it as
more popular idea pop up, so please continue.
Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a few
important messages that were mailed directly to internals, but hopefully
I've managed to fix that now.
On Wed, Apr 20, 2016 at 1:17 PM, Dominic Grostate <
codekestrel@googlemail.com> wrote:
Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or multiple
there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against the
addition of an "is" keyword, in favour of one of the other two.
I like instanceof the most, as I feel its more obvious. "is" is good IMO
from a user perspective, tho its a new keyword, its pretty obvious what's
happening (my T IS something, and will not be anything else). "as" makes me
feel like its going to either type-cast or T is going to be aliased as Bar
for later use in the class definition.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are approved,
then generics would have to support them as well. Likewise if this feature
becomes approved in generics, it would make sense to support them in
regular type hints as well.
Agreed, I think this should be left until after both RFCs have been decided
on.
The RFC makes a reference to generic closures, which may look something
like this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature at
https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each other
(please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude this
part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>". However to
ease the implementation it was decided that should be a separate feature.
So we'd like to find out if everyone else feels the same way about callable
types.
This makes sense to me to leave it separate. Lets get generic classes first.
This RFC currently doesn't specify in detail how reflection would work. We
have attempted a few API designs, but due to generic classes being ...
generic, it is difficult to find a suitable way to glean information about
a class in a backwards compatible manner. So we will need some help on
this one.
Aside from these top issues on our own list, however does everyone feel
about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it as
more popular idea pop up, so please continue.
I think the RFC looks pretty great. I think you guys have put in a lot of
very good work speccing it out, thanks for that. As generics are the
biggest thing I feel missing that I use elsewhere, I really hope this
change gets accepted and fully working implementation!
Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a few
important messages that were mailed directly to internals, but hopefully
I've managed to fix that now.
On Wed, Apr 20, 2016 at 12:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:
The preferred keywords appear to be either "as" or "instanceof".
class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against the
addition of an "is" keyword, in favour of one of the other two.
I'm against adding a new keyword anywhere we can trivially avoid it,
and this is one of those places where avoidance is trivial.
Preference for 'as', since it conform's to HackLang's generics (and
it's fewer keystrokes), but 'instanceof' has enough clarity to it that
I wouldn't complain.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are approved,
then generics would have to support them as well. Likewise if this feature
becomes approved in generics, it would make sense to support them in regular
type hints as well.
IMO. This feature is complex enough without unions/intersections, and
they have the concurrency problem with the fact that the Union Types
RFC is also under discussion. I think if both that RFC and this one
(without complex limits) both pass, then it makes sense to do a
followup RFC to combine them in logical ways.
The RFC makes a reference to generic closures, which may look something like
this:
function my_function(callable<Foo, Bar> $func)However, an RFC already exists which is very similar to this feature at
https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each other
(please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude this
part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>". However to
ease the implementation it was decided that should be a separate feature.
So we'd like to find out if everyone else feels the same way about callable
types.
Callable is probably a bad use-case for this extension of generics,
but I'd like to see array<Tk,Tv> at some later point. That said, both
should be considered out of scope of this RFC in my opionion.
This RFC currently doesn't specify in detail how reflection would work. We
have attempted a few API designs, but due to generic classes being ...
generic, it is difficult to find a suitable way to glean information about a
class in a backwards compatible manner. So we will need some help on this
one.
I don't think Reflection necessarily needs to be part of the RFC, but
if we can come up with something which makes sense it doesn't have to
be out of scope. Really, the in/ex-clusion of this part will come
down to whether or not someone comes up with a sane API for it.
Aside from these top issues on our own list, however does everyone feel
about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it as more
popular idea pop up, so please continue.
+1 from me. This is an essential piece of being able to do
comprehensive static analysis of PHP programs, and static analysis has
already saved my butt more times than I care to count.
-Sara
"instanceof" implies to me that the right hand side is a class/interface
name, since that's what's expected with the "instanceof" operator, rather
than a type. If I can do "Foo<T instanceof array<Bar>>" I would expect to
be able to do "if ($t instanceof array<Bar>) ...", but I can't.
I think unless the "instanceof" operator is updated to be able to do checks
with arbitrary type expressions, a different keyword should be used for
generic type constraints so as not to imply any misleading correspondence
with the "instanceof" operator.
So I would prefer "as". It's also shorter, and matches Hack.
On Thu, Apr 21, 2016 at 5:17 AM, Dominic Grostate <
codekestrel@googlemail.com> wrote:
Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or multiple
there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against the
addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are approved,
then generics would have to support them as well. Likewise if this feature
becomes approved in generics, it would make sense to support them in
regular type hints as well.
The RFC makes a reference to generic closures, which may look something
like this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature at
https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each other
(please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude this
part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>". However to
ease the implementation it was decided that should be a separate feature.
So we'd like to find out if everyone else feels the same way about callable
types.
This RFC currently doesn't specify in detail how reflection would work. We
have attempted a few API designs, but due to generic classes being ...
generic, it is difficult to find a suitable way to glean information about
a class in a backwards compatible manner. So we will need some help on
this one.
Aside from these top issues on our own list, however does everyone feel
about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it as
more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a few
important messages that were mailed directly to internals, but hopefully
I've managed to fix that now.
"instanceof" implies to me that the right hand side is a class/interface
name, since that's what's expected with the "instanceof" operator, rather
than a type. If I can do "Foo<T instanceof array<Bar>>" I would expect to
be able to do "if ($t instanceof array<Bar>) ...", but I can't.
I'm not quite sure what bounds would mean for anything other than
classes or interfaces. A generic type that specified that its type
parameter must be an int seems to me to be a non-generic type, because
nothing other than an int "is int". How is "class Foo<T is int> { public
function __construct(T $arg) { ... } }" different from just saying
"class Foo { public function __construct(int $arg) { ... } }"? (Unless
there were some kind of overloading, such that you could also have Foo<T
is float>, but please let's not go there...)
Note that generic arrays are out of scope of the current RFC anyway, so
it's possible that "$t instanceof array<Bar>" could be added at the same
time as "Foo<T instanceof array<Bar>>", presumably with the meaning that
"array<SubClassOfBar>" would be considered to pass the check.
Regards,
--
Rowan Collins
[IMSoP]
On Thu, Apr 21, 2016 at 8:56 AM, Rowan Collins rowan.collins@gmail.com
wrote:
I'm not quite sure what bounds would mean for anything other than classes
or interfaces. A generic type that specified that its type parameter must
be an int seems to me to be a non-generic type, because nothing other than
an int "is int". How is "class Foo<T is int> { public function
__construct(T $arg) { ... } }" different from just saying "class Foo {
public function __construct(int $arg) { ... } }"? (Unless there were some
kind of overloading, such that you could also have Foo<T is float>, but
please let's not go there...)
Sticking with your example for a moment, if the type parameter is an "int",
then the only type that can currently be expressed in the type language
that is a subtype of "int" is "int", so it's not very useful right now.
However, a possible introduction of integer literal types would change
that, especially in combination with unions. This would enable you to
express the type "1|2|3", i.e. only one of those three integers. This would
be useful to typecheck enums, which are typically an "int" or "string"
which only accepts a finite set of values. As such, the type parameter "T
is int" could be filled with "int" or "1" or "0|1|2". (Imagine a class
"Bar<T is int>" and various subclasses which each must select a single
integer as kind of tag, "Foo extends Bar<1>", "Baz extends Bar<2>" etc. I
do this in TypeScript with string literal types all the time.)
Beside from that, the introduction of any one of union types, intersection
types, generic array types or callable types would mean the type constraint
for a generic type can usefully be something besides a single
class/interface. For example, if "<T is array|Travarsable>" were written
"<T instanceof array|Traversable>" I would expect to be able to do "$t
instanceof array|Traversable", but I wouldn't be able to unless
"instanceof" is intended to and is extended to support arbitrary types.
Note that generic arrays are out of scope of the current RFC anyway, so
it's possible that "$t instanceof array<Bar>" could be added at the same
time as "Foo<T instanceof array<Bar>>", presumably with the meaning that
"array<SubClassOfBar>" would be considered to pass the check.Regards,
--
Rowan Collins
[IMSoP]
Jesse Schalken wrote on 21/04/2016 04:18:
Sticking with your example for a moment, if the type parameter is an
"int", then the only type that can /currently/ be expressed in the
type language that is a subtype of "int" is "int", so it's not very
useful /right now/. However, a possible introduction of integer
literal types would change that, especially in combination with
unions. This would enable you to express the type "1|2|3", i.e. only
one of those three integers. This would be useful to typecheck enums,
which are typically an "int" or "string" which only accepts a finite
set of values. As such, the type parameter "T is int" could be filled
with "int" or "1" or "0|1|2". (Imagine a class "Bar<T is int>" and
various subclasses which each must select a single integer as kind of
tag, "Foo extends Bar<1>", "Baz extends Bar<2>" etc. I do this in
TypeScript with string literal types all the time.)
I'm struggling to follow this example; it seems like you are using the
type parameter to supply actual data to the implementation, which goes
rather beyond generics, and into full-blown template metaprogramming.
I'm not sure why you'd ever need the sub-class to compile in a value
that way rather than just overriding a method somewhere, and would need
some convincing that PHP needed this kind of complexity.
Beside from that, the introduction of any one of union types,
intersection types, generic array types or callable types would mean
the type constraint for a generic type can usefully be something
besides a single class/interface. For example, if "<T is
array|Travarsable>" were written "<T instanceof array|Traversable>" I
would expect to be able to do "$t instanceof array|Traversable", but I
wouldn't be able to unless "instanceof" is intended to and is extended
to support arbitrary types.
This is a stronger argument, although I think extending the instanceof
operator might be a good idea in that case anyway - otherwise, we would
have a constraint that you can assert (produce a TypeError at a function
boundary) but not easily test for. To avoid replicating the engine's
entire checking routine, you'd end up doing this:
function ugly_instance_of<T>($value): boolean {
try {
return (function(T $checked) { return true; })($value);
}
catch ( TypeError $e ) {
return false;
}
}
if ( ugly_instance_of<array<Foo>>($x) ) // if ( $x instanceof array<Foo> )
shudder ;)
Regards,
Rowan Collins
[IMSoP]
I see that some of you are confusing union types with intersecting types
here. The idea is not an OR, but an AND.
I'll repeat the same example again to try to exemplify what I mentioned:
class AA {}
interface B {}
interface C {}
class BB extends AA implements B {}
class CC extends AA implements B, C {}
class Foo<T instanceof AA & B & C> {}
class Bar<T instanceof AA | B | C> {}
Class Foo means an intersecting type, where only class CC is applicable
there. Class Bar means a union type, whee both AA, BB and CC are applicable.
I understand others mentioned we could create a new interface that wraps
the other two interfaces, but that still doesn't solve the problem where I
want to extend a class AND also implement a specific interface.
Surely this can be a subsequent RFC, but I see that as a deficiency of the
currently proposed one and consider it incomplete. What happens if the
initial one get approved but it is too late to propose the subsequent one?
Then PHP 7.1 would be released with a partial implementation and not fully
featured as it should.
Regards,
On Wed, Apr 20, 2016 at 11:18 PM, Jesse Schalken me@jesseschalken.com
wrote:
On Thu, Apr 21, 2016 at 8:56 AM, Rowan Collins rowan.collins@gmail.com
wrote:I'm not quite sure what bounds would mean for anything other than classes
or interfaces. A generic type that specified that its type parameter must
be an int seems to me to be a non-generic type, because nothing other
than
an int "is int". How is "class Foo<T is int> { public function
__construct(T $arg) { ... } }" different from just saying "class Foo {
public function __construct(int $arg) { ... } }"? (Unless there were some
kind of overloading, such that you could also have Foo<T is float>, but
please let's not go there...)Sticking with your example for a moment, if the type parameter is an "int",
then the only type that can currently be expressed in the type language
that is a subtype of "int" is "int", so it's not very useful right now.
However, a possible introduction of integer literal types would change
that, especially in combination with unions. This would enable you to
express the type "1|2|3", i.e. only one of those three integers. This would
be useful to typecheck enums, which are typically an "int" or "string"
which only accepts a finite set of values. As such, the type parameter "T
is int" could be filled with "int" or "1" or "0|1|2". (Imagine a class
"Bar<T is int>" and various subclasses which each must select a single
integer as kind of tag, "Foo extends Bar<1>", "Baz extends Bar<2>" etc. I
do this in TypeScript with string literal types all the time.)Beside from that, the introduction of any one of union types, intersection
types, generic array types or callable types would mean the type constraint
for a generic type can usefully be something besides a single
class/interface. For example, if "<T is array|Travarsable>" were written
"<T instanceof array|Traversable>" I would expect to be able to do "$t
instanceof array|Traversable", but I wouldn't be able to unless
"instanceof" is intended to and is extended to support arbitrary types.Note that generic arrays are out of scope of the current RFC anyway, so
it's possible that "$t instanceof array<Bar>" could be added at the same
time as "Foo<T instanceof array<Bar>>", presumably with the meaning that
"array<SubClassOfBar>" would be considered to pass the check.Regards,
--
Rowan Collins
[IMSoP]--
--
Guilherme Blanco
Lead Architect at E-Block
Nope, it pretty much made perfect sense to me the first time. But you may
get your wish in it going in with the proposal anyway as I don't think this
RFC is going to make it in time for the 7.1 window.
My crack at implementing it went bust when I realised I was adding too many
execute globals.
Speaking of implementation though. Does anyone have a fairly good idea on
how to do it? I got the parser ambiguity out of the way, but my attempts
at building the complete feature were mostly experimental. So I'd like to
get an idea of how likely it is someone else will be able to take over.
On 21 Apr 2016 4:25 p.m., "guilhermeblanco@gmail.com" <
guilhermeblanco@gmail.com> wrote:
I see that some of you are confusing union types with intersecting types
here. The idea is not an OR, but an AND.
I'll repeat the same example again to try to exemplify what I mentioned:class AA {}
interface B {}
interface C {}
class BB extends AA implements B {}
class CC extends AA implements B, C {}class Foo<T instanceof AA & B & C> {}
class Bar<T instanceof AA | B | C> {}Class Foo means an intersecting type, where only class CC is applicable
there. Class Bar means a union type, whee both AA, BB and CC are
applicable.I understand others mentioned we could create a new interface that wraps
the other two interfaces, but that still doesn't solve the problem where I
want to extend a class AND also implement a specific interface.
Surely this can be a subsequent RFC, but I see that as a deficiency of the
currently proposed one and consider it incomplete. What happens if the
initial one get approved but it is too late to propose the subsequent one?
Then PHP 7.1 would be released with a partial implementation and not fully
featured as it should.Regards,
On Wed, Apr 20, 2016 at 11:18 PM, Jesse Schalken me@jesseschalken.com
wrote:On Thu, Apr 21, 2016 at 8:56 AM, Rowan Collins rowan.collins@gmail.com
wrote:I'm not quite sure what bounds would mean for anything other than
classes
or interfaces. A generic type that specified that its type parameter
must
be an int seems to me to be a non-generic type, because nothing other
than
an int "is int". How is "class Foo<T is int> { public function
__construct(T $arg) { ... } }" different from just saying "class Foo {
public function __construct(int $arg) { ... } }"? (Unless there were
some
kind of overloading, such that you could also have Foo<T is float>, but
please let's not go there...)Sticking with your example for a moment, if the type parameter is an
"int",
then the only type that can currently be expressed in the type language
that is a subtype of "int" is "int", so it's not very useful right now.
However, a possible introduction of integer literal types would change
that, especially in combination with unions. This would enable you to
express the type "1|2|3", i.e. only one of those three integers. This
would
be useful to typecheck enums, which are typically an "int" or "string"
which only accepts a finite set of values. As such, the type parameter "T
is int" could be filled with "int" or "1" or "0|1|2". (Imagine a class
"Bar<T is int>" and various subclasses which each must select a single
integer as kind of tag, "Foo extends Bar<1>", "Baz extends Bar<2>" etc. I
do this in TypeScript with string literal types all the time.)Beside from that, the introduction of any one of union types,
intersection
types, generic array types or callable types would mean the type
constraint
for a generic type can usefully be something besides a single
class/interface. For example, if "<T is array|Travarsable>" were written
"<T instanceof array|Traversable>" I would expect to be able to do "$t
instanceof array|Traversable", but I wouldn't be able to unless
"instanceof" is intended to and is extended to support arbitrary types.Note that generic arrays are out of scope of the current RFC anyway, so
it's possible that "$t instanceof array<Bar>" could be added at the
same
time as "Foo<T instanceof array<Bar>>", presumably with the meaning
that
"array<SubClassOfBar>" would be considered to pass the check.Regards,
--
Rowan Collins
[IMSoP]--
--
Guilherme Blanco
Lead Architect at E-Block
guilhermeblanco@gmail.com wrote on 21/04/2016 16:25:
I understand others mentioned we could create a new interface that
wraps the other two interfaces, but that still doesn't solve the
problem where I want to extend a class AND also implement a specific
interface.
I covered this case in my previous mail: you can create an abstract
class that extends a concrete class and adds an interface to it, and the
extra methods are implicitly added as abstract for descendants to implement:
// This could be somewhere else in code you don't want to touch
class Foo { public function doStuff() { /* ... */ } }
interface Bar { public function neededAction(); }
// Wherever you need to use the intersection Foo & Bar
abstract class FooBar extends Foo implements Bar {}
class Foo<T instanceof FooBar> { /* can rely on both T#doStuff and
T#neededAction existing */ }
// The implementation of the intersection must be in our control, to use
"FooBar" rather than "Foo" as a base
// This is also true of extending interfaces, since PHP doesn't do
duck-typing of interfaces
class FooBarImplementation extends FooBar { public function
neededAction() { /* ... */ } }
What happens if the initial one get approved but it is too late to
propose the subsequent one? Then PHP 7.1 would be released with a
partial implementation and not fully featured as it should.
No other part of the language allows such type algebra, so a generics
implementation that doesn't allow it wouldn't feel "incomplete" to
anyone who wasn't aware of the discussion. Do you consider the typehints
PHP has had for over a decade to be "a partial implementation" because
they don't include this feature?
I'm assuming that if generics passed, and type algebra passed, then
generics-with-type-algebra would go through on the nod and make it to
the same version.
Regards,
Rowan Collins
[IMSoP]
guilhermeblanco@gmail.com wrote on 21/04/2016 16:25:
I understand others mentioned we could create a new interface that
wraps the other two interfaces, but that still doesn't solve the
problem where I want to extend a class AND also implement a specific
interface.I covered this case in my previous mail: you can create an abstract
class that extends a concrete class and adds an interface to it, and
the extra methods are implicitly added as abstract for descendants to
implement:
Creating a one off class or interface to handle intersection cases works
iff you control all creation points for all objects that you may
encounter. That is rarely the case when dealing with 3rd party
libraries, as is now typical.
--
--Larry Garfield
Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or multiple
there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against the
addition of an "is" keyword, in favour of one of the other two.
I don't mind having a new keyword and is would be my first choice because
using one of the other two would feel odd. in T instanceof Bar I expect
T to be a
variable, not a type and with T as Bar I feel like I'm telling PHP "T is
not a Bar but
treat as if it is"
I'm not that well aware of the implication of adding a new keyword but I
thought
it would not be a big deal since
https://wiki.php.net/rfc/context_sensitive_lexer
has passed (I'm not saying it's not important, just that using the most
appropriate
keyword is very important too)
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are approved,
then generics would have to support them as well. Likewise if this feature
becomes approved in generics, it would make sense to support them in
regular type hints as well.
The RFC makes a reference to generic closures, which may look something
like this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature at
https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each other
(please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude this
part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>". However to
ease the implementation it was decided that should be a separate feature.
So we'd like to find out if everyone else feels the same way about callable
types.
This RFC currently doesn't specify in detail how reflection would work. We
have attempted a few API designs, but due to generic classes being ...
generic, it is difficult to find a suitable way to glean information about
a class in a backwards compatible manner. So we will need some help on
this one.
Aside from these top issues on our own list, however does everyone feel
about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it as
more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a few
important messages that were mailed directly to internals, but hopefully
I've managed to fix that now.
--
Mathieu Rochette
On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:
Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or multiple there
of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against the
addition of an "is" keyword, in favour of one of the other two.
I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.
On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:
Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or multiple there
of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against the
addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are approved,
then generics would have to support them as well. Likewise if this feature
becomes approved in generics, it would make sense to support them in regular
type hints as well.
The RFC makes a reference to generic closures, which may look something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature at
https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each other
(please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude this
part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>". However to
ease the implementation it was decided that should be a separate feature.
So we'd like to find out if everyone else feels the same way about callable
types.
This RFC currently doesn't specify in detail how reflection would work. We
have attempted a few API designs, but due to generic classes being ...
generic, it is difficult to find a suitable way to glean information about a
class in a backwards compatible manner. So we will need some help on this
one.
Aside from these top issues on our own list, however does everyone feel
about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it as more
popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a few
important messages that were mailed directly to internals, but hopefully
I've managed to fix that now.
I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.
I second that.
I hear the concerns about adding another reserved word "is" though,
so I'd like to suggest simply using a ":" ... as in:
class A<T : T1> { ... }
Consistent with return type-hints, it should feel like home?
For sure nobody wants to type out "instanceof", and (as pointed out
in the RFC) the instanceof operator checks the type of an object,
which is not what this is doing - a type argument is not an "instance
of" anything. The ":" is more neutral in that regard maybe?
On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or multiple there
of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against the
addition of an "is" keyword, in favour of one of the other two.I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or multiple there
of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against the
addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are approved,
then generics would have to support them as well. Likewise if this feature
becomes approved in generics, it would make sense to support them in regular
type hints as well.
The RFC makes a reference to generic closures, which may look something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature at
https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each other
(please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude this
part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>". However to
ease the implementation it was decided that should be a separate feature.
So we'd like to find out if everyone else feels the same way about callable
types.
This RFC currently doesn't specify in detail how reflection would work. We
have attempted a few API designs, but due to generic classes being ...
generic, it is difficult to find a suitable way to glean information about a
class in a backwards compatible manner. So we will need some help on this
one.
Aside from these top issues on our own list, however does everyone feel
about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it as more
popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a few
important messages that were mailed directly to internals, but hopefully
I've managed to fix that now.
-----Ursprüngliche Nachricht-----
Von: Rasmus Schultz [mailto:rasmus@mindplay.dk]
Gesendet: Montag, 25. April 2016 18:09
An: Josh Di Fabio
Cc: Dominic Grostate; Guilherme Blanco; Mathieu Rochette; Ben Scholzen 'DASPRiD'; Sara Golemon; PHP internals; Mathieu
Rochette
Betreff: Re: [PHP-DEV] [RFC:generics]I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.I second that.
I hear the concerns about adding another reserved word "is" though, so I'd like to suggest simply using a ":" ... as in:
class A<T : T1> { ... }
In this case I would suggest to use class A<T <: T1> which leaves room open to define lower bounds later on (either with <: as well or with :> as in scala)
Consistent with return type-hints, it should feel like home?
For sure nobody wants to type out "instanceof", and (as pointed out in the RFC) the instanceof operator checks the type of
an object, which is not what this is doing - a type argument is not an "instance of" anything. The ":" is more neutral in that
regard maybe?On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are
approved, then generics would have to support them as well. Likewise
if this feature becomes approved in generics, it would make sense to
support them in regular type hints as well.
The RFC makes a reference to generic closures, which may look
something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature
at https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each
other (please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude
this part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>".
However to ease the implementation it was decided that should be a separate feature.
So we'd like to find out if everyone else feels the same way about
callable types.
This RFC currently doesn't specify in detail how reflection would
work. We have attempted a few API designs, but due to generic classes being ...
generic, it is difficult to find a suitable way to glean information
about a class in a backwards compatible manner. So we will need some
help on this one.
Aside from these top issues on our own list, however does everyone
feel about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it
as more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a
few important messages that were mailed directly to internals, but
hopefully I've managed to fix that now.
Hi all,
Yesterday I spent a considerable 2h talking about Generics in Doctrine
channel.
We discussed the specifics of each boundary that PHP's implementation could
take advantage. Here are our findings, which I'll illustrate using Java
equivalents:
1- Upper bounds (T extends A)
We all understood they're required. Whenever you mention class Foo<T is A>
{}, we're talking that T can be A or any of its subtypes.
Also, we debated over intersection types. It's an edge case, and its
support could be done as a subsequent RFC once union and intersection types
gets resolved.
2- Lower bounds (T super A)
It was just not possible to come up with a single use case or possibility.
It not only violates Liskov, but it also doesn't make any sense in the
context of PHP.
When we debate about Java, it does make sense because of polymorphism and
the requirement removal of implementing multiple methods.
3- Unbounded wildcards (?)
It wouldn't be necessary in the context of PHP. Why?
Once we introduce Generics, the difference between process(List $list) and
process(List<?> $list) would be none, as due to PHP's nature.
4- Upper bounded wildcard (? extends A)
Again, invalid in the context of PHP. Let me explain why...
In Java context, whenever you declare something as List<A>, you can only
add As to the list, but no subtypes. When you declare List<? extends A>,
you can add A and also any of its subtypes.
PHP is loose in this restriction, so there's no way of strict-ing to only A
but not subtypes. Defining as List<A> would be enough, and PHP wouldn't
support adding A and A only.
5- Lower bounded wildcard (? super A)
Applies the same concept of #2. PHP doesn't need it as it doesn't fully
support polymorphism.
6- Reflection
We discussed over an example I extracted from a piece of code I currently
work on. We came with several ideas, but couldn't wrap up (but we're 80%) a
valid approach. The example we debated was this one:
https://gist.github.com/guilhermeblanco/56ec0e11e7b029c2cfdcaf6fe2323742
So I'll have to say sorry for poking around of "missing implementation"
while in reality most of them cannot be applied in the context of PHP.
I've reviewed the RFC again and it mostly makes sense surrounding
boundaries. I still have to talk about diamond operator and constructor
generic type.
[]s,
-----Ursprüngliche Nachricht-----
Von: Rasmus Schultz [mailto:rasmus@mindplay.dk]
Gesendet: Montag, 25. April 2016 18:09
An: Josh Di Fabio
Cc: Dominic Grostate; Guilherme Blanco; Mathieu Rochette; Ben Scholzen
'DASPRiD'; Sara Golemon; PHP internals; Mathieu
Rochette
Betreff: Re: [PHP-DEV] [RFC:generics]I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.I second that.
I hear the concerns about adding another reserved word "is" though, so
I'd like to suggest simply using a ":" ... as in:class A<T : T1> { ... }
In this case I would suggest to use class A<T <: T1> which leaves room
open to define lower bounds later on (either with <: as well or with :> as
in scala)Consistent with return type-hints, it should feel like home?
For sure nobody wants to type out "instanceof", and (as pointed out in
the RFC) the instanceof operator checks the type of
an object, which is not what this is doing - a type argument is not an
"instance of" anything. The ":" is more neutral in that
regard maybe?On Thu, Apr 21, 2016 at 10:32 AM, Josh Di Fabio joshdifabio@gmail.com
wrote:On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are
approved, then generics would have to support them as well. Likewise
if this feature becomes approved in generics, it would make sense to
support them in regular type hints as well.
The RFC makes a reference to generic closures, which may look
something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature
at https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each
other (please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude
this part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>".
However to ease the implementation it was decided that should be a
separate feature.
So we'd like to find out if everyone else feels the same way about
callable types.
This RFC currently doesn't specify in detail how reflection would
work. We have attempted a few API designs, but due to generic
classes being ...
generic, it is difficult to find a suitable way to glean information
about a class in a backwards compatible manner. So we will need some
help on this one.
Aside from these top issues on our own list, however does everyone
feel about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it
as more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a
few important messages that were mailed directly to internals, but
hopefully I've managed to fix that now.--
To unsubscribe,
visit: http://www.php.net/unsub.php
--
Guilherme Blanco
Lead Architect at E-Block
On Wed, Apr 27, 2016 at 6:50 AM, guilhermeblanco@gmail.com <
guilhermeblanco@gmail.com> wrote:
Hi all,
Yesterday I spent a considerable 2h talking about Generics in Doctrine
channel.
We discussed the specifics of each boundary that PHP's implementation could
take advantage. Here are our findings, which I'll illustrate using Java
equivalents:1- Upper bounds (T extends A)
We all understood they're required. Whenever you mention class Foo<T is A>
{}, we're talking that T can be A or any of its subtypes.
Also, we debated over intersection types. It's an edge case, and its
support could be done as a subsequent RFC once union and intersection types
gets resolved.2- Lower bounds (T super A)
It was just not possible to come up with a single use case or possibility.
It not only violates Liskov, but it also doesn't make any sense in the
context of PHP.
When we debate about Java, it does make sense because of polymorphism and
the requirement removal of implementing multiple methods.
I didn't understand the point of lower bound until I read this:
http://hhvm.com/blog/9215/covariance-contravariance-and-super-type-constraints
For example, this is a correctly typed array_merge()
(ignoring the fact
that it's variadic, for simplicity):
function array_merge<T>(array<T> $a, array<T> $b):array<T> { ... }
(Keeping in mind that array<T> is covariant in T, so I can do
array_merge([new A()], [new B()]) and get an array<C>, provided A and B are
compatible with C.)
But if you have a class which has similar functionality, you would have to
type it as:
class Foo<T> {
public function merge<V super T>(Foo<V> $o):Foo<V> { ... }
}
because the type of the result doesn't have to be Foo<T>, it can be Foo of
anything (V), as long as T of the current class is compatible with it (V
super T).
3- Unbounded wildcards (?)
It wouldn't be necessary in the context of PHP. Why?
Once we introduce Generics, the difference between process(List $list) and
process(List<?> $list) would be none, as due to PHP's nature.4- Upper bounded wildcard (? extends A)
Again, invalid in the context of PHP. Let me explain why...
In Java context, whenever you declare something as List<A>, you can only
add As to the list, but no subtypes. When you declare List<? extends A>,
you can add A and also any of its subtypes.
PHP is loose in this restriction, so there's no way of strict-ing to only A
but not subtypes. Defining as List<A> would be enough, and PHP wouldn't
support adding A and A only.5- Lower bounded wildcard (? super A)
Applies the same concept of #2. PHP doesn't need it as it doesn't fully
support polymorphism.6- Reflection
We discussed over an example I extracted from a piece of code I currently
work on. We came with several ideas, but couldn't wrap up (but we're 80%) a
valid approach. The example we debated was this one:
https://gist.github.com/guilhermeblanco/56ec0e11e7b029c2cfdcaf6fe2323742So I'll have to say sorry for poking around of "missing implementation"
while in reality most of them cannot be applied in the context of PHP.
I've reviewed the RFC again and it mostly makes sense surrounding
boundaries. I still have to talk about diamond operator and constructor
generic type.[]s,
-----Ursprüngliche Nachricht-----
Von: Rasmus Schultz [mailto:rasmus@mindplay.dk]
Gesendet: Montag, 25. April 2016 18:09
An: Josh Di Fabio
Cc: Dominic Grostate; Guilherme Blanco; Mathieu Rochette; Ben Scholzen
'DASPRiD'; Sara Golemon; PHP internals; Mathieu
Rochette
Betreff: Re: [PHP-DEV] [RFC:generics]I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.I second that.
I hear the concerns about adding another reserved word "is" though, so
I'd like to suggest simply using a ":" ... as in:class A<T : T1> { ... }
In this case I would suggest to use class A<T <: T1> which leaves room
open to define lower bounds later on (either with <: as well or with :>
as
in scala)Consistent with return type-hints, it should feel like home?
For sure nobody wants to type out "instanceof", and (as pointed out in
the RFC) the instanceof operator checks the type of
an object, which is not what this is doing - a type argument is not
an
"instance of" anything. The ":" is more neutral in that
regard maybe?On Thu, Apr 21, 2016 at 10:32 AM, Josh Di Fabio <joshdifabio@gmail.com
wrote:
On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are
approved, then generics would have to support them as well.
Likewise
if this feature becomes approved in generics, it would make sense to
support them in regular type hints as well.
The RFC makes a reference to generic closures, which may look
something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature
at https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each
other (please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude
this part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>".
However to ease the implementation it was decided that should be a
separate feature.
So we'd like to find out if everyone else feels the same way about
callable types.
This RFC currently doesn't specify in detail how reflection would
work. We have attempted a few API designs, but due to generic
classes being ...
generic, it is difficult to find a suitable way to glean information
about a class in a backwards compatible manner. So we will need
some
help on this one.
Aside from these top issues on our own list, however does everyone
feel about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it
as more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a
few important messages that were mailed directly to internals, but
hopefully I've managed to fix that now.--
To unsubscribe,
visit: http://www.php.net/unsub.php--
Guilherme Blanco
Lead Architect at E-Block
Thanks Guilherme - this pretty much sums up how I feel about features 2-5.
Plain generics with upper bounds, I find some case where it's missing,
almost daily.
As for the gist you posted, I'm lost, what does this have to do with reflection?
On Tue, Apr 26, 2016 at 10:50 PM, guilhermeblanco@gmail.com
guilhermeblanco@gmail.com wrote:
Hi all,
Yesterday I spent a considerable 2h talking about Generics in Doctrine
channel.
We discussed the specifics of each boundary that PHP's implementation could
take advantage. Here are our findings, which I'll illustrate using Java
equivalents:1- Upper bounds (T extends A)
We all understood they're required. Whenever you mention class Foo<T is A>
{}, we're talking that T can be A or any of its subtypes.
Also, we debated over intersection types. It's an edge case, and its support
could be done as a subsequent RFC once union and intersection types gets
resolved.2- Lower bounds (T super A)
It was just not possible to come up with a single use case or possibility.
It not only violates Liskov, but it also doesn't make any sense in the
context of PHP.
When we debate about Java, it does make sense because of polymorphism and
the requirement removal of implementing multiple methods.3- Unbounded wildcards (?)
It wouldn't be necessary in the context of PHP. Why?
Once we introduce Generics, the difference between process(List $list) and
process(List<?> $list) would be none, as due to PHP's nature.4- Upper bounded wildcard (? extends A)
Again, invalid in the context of PHP. Let me explain why...
In Java context, whenever you declare something as List<A>, you can only add
As to the list, but no subtypes. When you declare List<? extends A>, you can
add A and also any of its subtypes.
PHP is loose in this restriction, so there's no way of strict-ing to only A
but not subtypes. Defining as List<A> would be enough, and PHP wouldn't
support adding A and A only.5- Lower bounded wildcard (? super A)
Applies the same concept of #2. PHP doesn't need it as it doesn't fully
support polymorphism.6- Reflection
We discussed over an example I extracted from a piece of code I currently
work on. We came with several ideas, but couldn't wrap up (but we're 80%) a
valid approach. The example we debated was this one:
https://gist.github.com/guilhermeblanco/56ec0e11e7b029c2cfdcaf6fe2323742So I'll have to say sorry for poking around of "missing implementation"
while in reality most of them cannot be applied in the context of PHP.
I've reviewed the RFC again and it mostly makes sense surrounding
boundaries. I still have to talk about diamond operator and constructor
generic type.[]s,
-----Ursprüngliche Nachricht-----
Von: Rasmus Schultz [mailto:rasmus@mindplay.dk]
Gesendet: Montag, 25. April 2016 18:09
An: Josh Di Fabio
Cc: Dominic Grostate; Guilherme Blanco; Mathieu Rochette; Ben Scholzen
'DASPRiD'; Sara Golemon; PHP internals; Mathieu
Rochette
Betreff: Re: [PHP-DEV] [RFC:generics]I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.I second that.
I hear the concerns about adding another reserved word "is" though, so
I'd like to suggest simply using a ":" ... as in:class A<T : T1> { ... }
In this case I would suggest to use class A<T <: T1> which leaves room
open to define lower bounds later on (either with <: as well or with :> as
in scala)Consistent with return type-hints, it should feel like home?
For sure nobody wants to type out "instanceof", and (as pointed out in
the RFC) the instanceof operator checks the type of
an object, which is not what this is doing - a type argument is not an
"instance of" anything. The ":" is more neutral in that
regard maybe?On Thu, Apr 21, 2016 at 10:32 AM, Josh Di Fabio joshdifabio@gmail.com
wrote:On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are
approved, then generics would have to support them as well. Likewise
if this feature becomes approved in generics, it would make sense to
support them in regular type hints as well.
The RFC makes a reference to generic closures, which may look
something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature
at https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each
other (please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude
this part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>".
However to ease the implementation it was decided that should be a
separate feature.
So we'd like to find out if everyone else feels the same way about
callable types.
This RFC currently doesn't specify in detail how reflection would
work. We have attempted a few API designs, but due to generic
classes being ...
generic, it is difficult to find a suitable way to glean information
about a class in a backwards compatible manner. So we will need some
help on this one.
Aside from these top issues on our own list, however does everyone
feel about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it
as more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a
few important messages that were mailed directly to internals, but
hopefully I've managed to fix that now.--
To unsubscribe,
visit: http://www.php.net/unsub.php--
Guilherme Blanco
Lead Architect at E-Block
On Wed, Apr 27, 2016 at 6:50 AM, guilhermeblanco@gmail.com <
guilhermeblanco@gmail.com> wrote:
Hi all,
Yesterday I spent a considerable 2h talking about Generics in Doctrine
channel.
We discussed the specifics of each boundary that PHP's implementation could
take advantage. Here are our findings, which I'll illustrate using Java
equivalents:1- Upper bounds (T extends A)
We all understood they're required. Whenever you mention class Foo<T is A>
{}, we're talking that T can be A or any of its subtypes.
Also, we debated over intersection types. It's an edge case, and its
support could be done as a subsequent RFC once union and intersection types
gets resolved.2- Lower bounds (T super A)
It was just not possible to come up with a single use case or possibility.
It not only violates Liskov, but it also doesn't make any sense in the
context of PHP.
When we debate about Java, it does make sense because of polymorphism and
the requirement removal of implementing multiple methods.3- Unbounded wildcards (?)
It wouldn't be necessary in the context of PHP. Why?
Once we introduce Generics, the difference between process(List $list) and
process(List<?> $list) would be none, as due to PHP's nature.4- Upper bounded wildcard (? extends A)
Again, invalid in the context of PHP. Let me explain why...
In Java context, whenever you declare something as List<A>, you can only
add As to the list, but no subtypes. When you declare List<? extends A>,
you can add A and also any of its subtypes.
PHP is loose in this restriction, so there's no way of strict-ing to only A
but not subtypes. Defining as List<A> would be enough, and PHP wouldn't
support adding A and A only.
You can add subtypes of A to a List<A> in Java. What List<? extends A>
means is that the list itself may be a list of any type, provided that type
is compatible with A. So if B extends A, List<B> is compatible with List<?
extends A>, and when reading items you can assume they will be compatible
with A (since B extends A) but you can't add an A (because it's actually a
list of Bs).
Similarly, List<? super A> means it may be a list of A or some super type
of A. So if A extends C, List<C> is compatible with List<? super A>, and
you can add an A (because A is compatible with C) but when reading items
you can't assume they're going to be As (because it's actually a list of
Cs).
This is my understanding of variance behaviour and notation in the three
languages I'm most familiar with:
Covariance (Foo<Sub> is a subtype of Foo<Super>)
Java Foo<? extends T> at usage Methods accepting T as parameter cannot be
called
Hack Foo<+T> at declaration Methods accepting T as parameter cannot be
defined
C# Foo<out T> at declaration Methods accepting T as parameter cannot be
defined
Contravariance (Foo<Super> is a subtype of Foo<Sub>)
Java Foo<? super T> at usage Methods returning T can be called but
return Object
Hack Foo<-T> at declaration Methods returning T cannot be defined
C# Foo<in T> at declaration Methods returning T cannot be defined
Invariance (no relationship between Foo<Super> and Foo<Sub>)
Java Foo<T> at usage Methods accepting T and returning T can
both be called
Hack Foo<T> at declaration Methods accepting T and returning T can both be
defined
C# Foo<T> at declaration Methods accepting T and returning T can both be
defined
(There are more general rules for method signatures when T is used as the
parameter for another generic type, but I can't remember what they are.)
I can't see variance mentioned in the RFC, but when I asked about it when
the RFC was posted on Reddit, Rasmus said the type parameters would always
be covariant, so Foo<A> in PHP as an annotation would be equivalent to
Foo<? extends A> in Java, even though this has the opposite of the intended
effect if Foo has methods which accept a parameter of the generic type,
such as something like List<T>::add(T $element), Callback<T>::run(T $data)
or Box<T>::setContents(T $contents).
My view is that PHP's generics should be invariant to start with (generic
arrays can still be covariant of course, since they are passed by value),
maintaining type safety and matching the behaviour of Java, Hack and C# for
type parameters without variance annotations, and variance syntax should be
added later as another RFC as demand arises.
5- Lower bounded wildcard (? super A)
Applies the same concept of #2. PHP doesn't need it as it doesn't fully
support polymorphism.6- Reflection
We discussed over an example I extracted from a piece of code I currently
work on. We came with several ideas, but couldn't wrap up (but we're 80%) a
valid approach. The example we debated was this one:
https://gist.github.com/guilhermeblanco/56ec0e11e7b029c2cfdcaf6fe2323742So I'll have to say sorry for poking around of "missing implementation"
while in reality most of them cannot be applied in the context of PHP.
I've reviewed the RFC again and it mostly makes sense surrounding
boundaries. I still have to talk about diamond operator and constructor
generic type.[]s,
-----Ursprüngliche Nachricht-----
Von: Rasmus Schultz [mailto:rasmus@mindplay.dk]
Gesendet: Montag, 25. April 2016 18:09
An: Josh Di Fabio
Cc: Dominic Grostate; Guilherme Blanco; Mathieu Rochette; Ben Scholzen
'DASPRiD'; Sara Golemon; PHP internals; Mathieu
Rochette
Betreff: Re: [PHP-DEV] [RFC:generics]I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.I second that.
I hear the concerns about adding another reserved word "is" though, so
I'd like to suggest simply using a ":" ... as in:class A<T : T1> { ... }
In this case I would suggest to use class A<T <: T1> which leaves room
open to define lower bounds later on (either with <: as well or with :>
as
in scala)Consistent with return type-hints, it should feel like home?
For sure nobody wants to type out "instanceof", and (as pointed out in
the RFC) the instanceof operator checks the type of
an object, which is not what this is doing - a type argument is not
an
"instance of" anything. The ":" is more neutral in that
regard maybe?On Thu, Apr 21, 2016 at 10:32 AM, Josh Di Fabio <joshdifabio@gmail.com
wrote:
On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are
approved, then generics would have to support them as well.
Likewise
if this feature becomes approved in generics, it would make sense to
support them in regular type hints as well.
The RFC makes a reference to generic closures, which may look
something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature
at https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each
other (please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude
this part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>".
However to ease the implementation it was decided that should be a
separate feature.
So we'd like to find out if everyone else feels the same way about
callable types.
This RFC currently doesn't specify in detail how reflection would
work. We have attempted a few API designs, but due to generic
classes being ...
generic, it is difficult to find a suitable way to glean information
about a class in a backwards compatible manner. So we will need
some
help on this one.
Aside from these top issues on our own list, however does everyone
feel about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it
as more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a
few important messages that were mailed directly to internals, but
hopefully I've managed to fix that now.--
To unsubscribe,
visit: http://www.php.net/unsub.php--
Guilherme Blanco
Lead Architect at E-Block
On Fri, Apr 29, 2016 at 4:55 AM, Jesse Schalken me@jesseschalken.com
wrote:
On Wed, Apr 27, 2016 at 6:50 AM, guilhermeblanco@gmail.com <
guilhermeblanco@gmail.com> wrote:Hi all,
Yesterday I spent a considerable 2h talking about Generics in Doctrine
channel.
We discussed the specifics of each boundary that PHP's implementation
could
take advantage. Here are our findings, which I'll illustrate using Java
equivalents:1- Upper bounds (T extends A)
We all understood they're required. Whenever you mention class Foo<T is A>
{}, we're talking that T can be A or any of its subtypes.
Also, we debated over intersection types. It's an edge case, and its
support could be done as a subsequent RFC once union and intersection
types
gets resolved.2- Lower bounds (T super A)
It was just not possible to come up with a single use case or possibility.
It not only violates Liskov, but it also doesn't make any sense in the
context of PHP.
When we debate about Java, it does make sense because of polymorphism and
the requirement removal of implementing multiple methods.3- Unbounded wildcards (?)
It wouldn't be necessary in the context of PHP. Why?
Once we introduce Generics, the difference between process(List $list) and
process(List<?> $list) would be none, as due to PHP's nature.4- Upper bounded wildcard (? extends A)
Again, invalid in the context of PHP. Let me explain why...
In Java context, whenever you declare something as List<A>, you can only
add As to the list, but no subtypes. When you declare List<? extends A>,
you can add A and also any of its subtypes.
PHP is loose in this restriction, so there's no way of strict-ing to only
A
but not subtypes. Defining as List<A> would be enough, and PHP wouldn't
support adding A and A only.You can add subtypes of A to a List<A> in Java. What List<? extends A>
means is that the list itself may be a list of any type, provided that type
is compatible with A. So if B extends A, List<B> is compatible with List<?
extends A>, and when reading items you can assume they will be compatible
with A (since B extends A) but you can't add an A (because it's actually a
list of Bs).
Wrong. This is documented here
https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html and
specifically states:
To write the method that works on lists of Number and the subtypes of Number,
such as Integer, Double, and Float, you would specify List<? extends
Number>. The term List<Number> is more restrictive than List<? extends
Number> because the former matches a list of type Number only, whereas
the latter matches a list of type Number or any of its subclasses.
Similarly, List<? super A> means it may be a list of A or some super type
of A. So if A extends C, List<C> is compatible with List<? super A>, and
you can add an A (because A is compatible with C) but when reading items
you can't assume they're going to be As (because it's actually a list of
Cs).
Again wrong. The lower bound also respect the rules of upper bound
wildcard. If you return a List<C>, you cannot add an A instance.
This is my understanding of variance behaviour and notation in the three
languages I'm most familiar with:Covariance (Foo<Sub> is a subtype of Foo<Super>)
Java Foo<? extends T> at usage Methods accepting T as parameter cannot
be called
Hack Foo<+T> at declaration Methods accepting T as parameter cannot
be defined
C# Foo<out T> at declaration Methods accepting T as parameter cannot
be definedContravariance (Foo<Super> is a subtype of Foo<Sub>)
Java Foo<? super T> at usage Methods returning T can be called but
return Object
Hack Foo<-T> at declaration Methods returning T cannot be defined
C# Foo<in T> at declaration Methods returning T cannot be definedInvariance (no relationship between Foo<Super> and Foo<Sub>)
Java Foo<T> at usage Methods accepting T and returning T can
both be called
Hack Foo<T> at declaration Methods accepting T and returning T can
both be defined
C# Foo<T> at declaration Methods accepting T and returning T can
both be defined(There are more general rules for method signatures when T is used as the
parameter for another generic type, but I can't remember what they are.)I can't see variance mentioned in the RFC, but when I asked about it when
the RFC was posted on Reddit, Rasmus said the type parameters would always
be covariant, so Foo<A> in PHP as an annotation would be equivalent to
Foo<? extends A> in Java, even though this has the opposite of the
intended effect if Foo has methods which accept a parameter of the generic
type, such as something like List<T>::add(T $element), Callback<T>::run(T
$data) or Box<T>::setContents(T $contents).
Rasmus is purely following already defined PHP covariance definition that
currently exists for type hinting into generic types.
Java differs from that and accept covariance for type definitions, but
restricts for generic types.
My view is that PHP's generics should be invariant to start with (generic
arrays can still be covariant of course, since they are passed by value),
maintaining type safety and matching the behaviour of Java, Hack and C# for
type parameters without variance annotations, and variance syntax should be
added later as another RFC as demand arises.5- Lower bounded wildcard (? super A)
Applies the same concept of #2. PHP doesn't need it as it doesn't fully
support polymorphism.6- Reflection
We discussed over an example I extracted from a piece of code I currently
work on. We came with several ideas, but couldn't wrap up (but we're 80%)
a
valid approach. The example we debated was this one:
https://gist.github.com/guilhermeblanco/56ec0e11e7b029c2cfdcaf6fe2323742So I'll have to say sorry for poking around of "missing implementation"
while in reality most of them cannot be applied in the context of PHP.
I've reviewed the RFC again and it mostly makes sense surrounding
boundaries. I still have to talk about diamond operator and constructor
generic type.[]s,
-----Ursprüngliche Nachricht-----
Von: Rasmus Schultz [mailto:rasmus@mindplay.dk]
Gesendet: Montag, 25. April 2016 18:09
An: Josh Di Fabio
Cc: Dominic Grostate; Guilherme Blanco; Mathieu Rochette; Ben Scholzen
'DASPRiD'; Sara Golemon; PHP internals; Mathieu
Rochette
Betreff: Re: [PHP-DEV] [RFC:generics]I really don't like 'as' in this context, even if Hack uses it, as
it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.I second that.
I hear the concerns about adding another reserved word "is" though, so
I'd like to suggest simply using a ":" ... as in:class A<T : T1> { ... }
In this case I would suggest to use class A<T <: T1> which leaves room
open to define lower bounds later on (either with <: as well or with :>
as
in scala)Consistent with return type-hints, it should feel like home?
For sure nobody wants to type out "instanceof", and (as pointed out in
the RFC) the instanceof operator checks the type of
an object, which is not what this is doing - a type argument is not
an
"instance of" anything. The ":" is more neutral in that
regard maybe?On Thu, Apr 21, 2016 at 10:32 AM, Josh Di Fabio <
joshdifabio@gmail.com>
wrote:On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.I really don't like 'as' in this context, even if Hack uses it, as
it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are
approved, then generics would have to support them as well.
Likewise
if this feature becomes approved in generics, it would make sense
to
support them in regular type hints as well.
The RFC makes a reference to generic closures, which may look
something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this
feature
at https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each
other (please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude
this part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>".
However to ease the implementation it was decided that should be a
separate feature.
So we'd like to find out if everyone else feels the same way about
callable types.
This RFC currently doesn't specify in detail how reflection would
work. We have attempted a few API designs, but due to generic
classes being ...
generic, it is difficult to find a suitable way to glean
information
about a class in a backwards compatible manner. So we will need
some
help on this one.
Aside from these top issues on our own list, however does everyone
feel about the proposal in general?
As the RFC is still in draft, we will continue to make changes to
it
as more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a
few important messages that were mailed directly to internals, but
hopefully I've managed to fix that now.--
To unsubscribe,
visit: http://www.php.net/unsub.php--
Guilherme Blanco
Lead Architect at E-Block
--
Guilherme Blanco
Lead Architect at E-Block
Sorry for top-posting... it looks like GMail top-posts everything that
doesn't have a reply character right before the inherited (replied email),
which i just did.
On Fri, Apr 29, 2016 at 10:26 AM, guilhermeblanco@gmail.com <
guilhermeblanco@gmail.com> wrote:
On Fri, Apr 29, 2016 at 4:55 AM, Jesse Schalken me@jesseschalken.com
wrote:On Wed, Apr 27, 2016 at 6:50 AM, guilhermeblanco@gmail.com <
guilhermeblanco@gmail.com> wrote:Hi all,
Yesterday I spent a considerable 2h talking about Generics in Doctrine
channel.
We discussed the specifics of each boundary that PHP's implementation
could
take advantage. Here are our findings, which I'll illustrate using Java
equivalents:1- Upper bounds (T extends A)
We all understood they're required. Whenever you mention class Foo<T is A>
{}, we're talking that T can be A or any of its subtypes.
Also, we debated over intersection types. It's an edge case, and its
support could be done as a subsequent RFC once union and intersection
types
gets resolved.2- Lower bounds (T super A)
It was just not possible to come up with a single use case or
possibility.
It not only violates Liskov, but it also doesn't make any sense in the
context of PHP.
When we debate about Java, it does make sense because of polymorphism and
the requirement removal of implementing multiple methods.3- Unbounded wildcards (?)
It wouldn't be necessary in the context of PHP. Why?
Once we introduce Generics, the difference between process(List $list)
and
process(List<?> $list) would be none, as due to PHP's nature.4- Upper bounded wildcard (? extends A)
Again, invalid in the context of PHP. Let me explain why...
In Java context, whenever you declare something as List<A>, you can only
add As to the list, but no subtypes. When you declare List<? extends A>,
you can add A and also any of its subtypes.
PHP is loose in this restriction, so there's no way of strict-ing to
only A
but not subtypes. Defining as List<A> would be enough, and PHP wouldn't
support adding A and A only.You can add subtypes of A to a List<A> in Java. What List<? extends A>
means is that the list itself may be a list of any type, provided that type
is compatible with A. So if B extends A, List<B> is compatible with List<?
extends A>, and when reading items you can assume they will be compatible
with A (since B extends A) but you can't add an A (because it's actually a
list of Bs).Wrong. This is documented here
https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html
and specifically states:To write the method that works on lists of Number and the subtypes of
Number, such as Integer, Double, and Float, you would specify List<?
extends Number>. The term List<Number> is more restrictive than List<?
extends Number> because the former matches a list of type Number only,
whereas the latter matches a list of type Number or any of its
subclasses.Similarly, List<? super A> means it may be a list of A or some super type
of A. So if A extends C, List<C> is compatible with List<? super A>, and
you can add an A (because A is compatible with C) but when reading items
you can't assume they're going to be As (because it's actually a list of
Cs).Again wrong. The lower bound also respect the rules of upper bound
wildcard. If you return a List<C>, you cannot add an A instance.This is my understanding of variance behaviour and notation in the three
languages I'm most familiar with:Covariance (Foo<Sub> is a subtype of Foo<Super>)
Java Foo<? extends T> at usage Methods accepting T as parameter cannot
be called
Hack Foo<+T> at declaration Methods accepting T as parameter cannot
be defined
C# Foo<out T> at declaration Methods accepting T as parameter cannot
be definedContravariance (Foo<Super> is a subtype of Foo<Sub>)
Java Foo<? super T> at usage Methods returning T can be called but
return Object
Hack Foo<-T> at declaration Methods returning T cannot be defined
C# Foo<in T> at declaration Methods returning T cannot be definedInvariance (no relationship between Foo<Super> and Foo<Sub>)
Java Foo<T> at usage Methods accepting T and returning T can
both be called
Hack Foo<T> at declaration Methods accepting T and returning T can
both be defined
C# Foo<T> at declaration Methods accepting T and returning T can
both be defined(There are more general rules for method signatures when T is used as the
parameter for another generic type, but I can't remember what they are.)I can't see variance mentioned in the RFC, but when I asked about it when
the RFC was posted on Reddit, Rasmus said the type parameters would always
be covariant, so Foo<A> in PHP as an annotation would be equivalent to
Foo<? extends A> in Java, even though this has the opposite of the
intended effect if Foo has methods which accept a parameter of the generic
type, such as something like List<T>::add(T $element), Callback<T>::run(T
$data) or Box<T>::setContents(T $contents).Rasmus is purely following already defined PHP covariance definition that
currently exists for type hinting into generic types.
Java differs from that and accept covariance for type definitions, but
restricts for generic types.My view is that PHP's generics should be invariant to start with (generic
arrays can still be covariant of course, since they are passed by value),
maintaining type safety and matching the behaviour of Java, Hack and C# for
type parameters without variance annotations, and variance syntax should be
added later as another RFC as demand arises.5- Lower bounded wildcard (? super A)
Applies the same concept of #2. PHP doesn't need it as it doesn't fully
support polymorphism.6- Reflection
We discussed over an example I extracted from a piece of code I currently
work on. We came with several ideas, but couldn't wrap up (but we're
80%) a
valid approach. The example we debated was this one:
https://gist.github.com/guilhermeblanco/56ec0e11e7b029c2cfdcaf6fe2323742So I'll have to say sorry for poking around of "missing implementation"
while in reality most of them cannot be applied in the context of PHP.
I've reviewed the RFC again and it mostly makes sense surrounding
boundaries. I still have to talk about diamond operator and constructor
generic type.[]s,
-----Ursprüngliche Nachricht-----
Von: Rasmus Schultz [mailto:rasmus@mindplay.dk]
Gesendet: Montag, 25. April 2016 18:09
An: Josh Di Fabio
Cc: Dominic Grostate; Guilherme Blanco; Mathieu Rochette; Ben
Scholzen
'DASPRiD'; Sara Golemon; PHP internals; Mathieu
Rochette
Betreff: Re: [PHP-DEV] [RFC:generics]I really don't like 'as' in this context, even if Hack uses it, as
it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.I second that.
I hear the concerns about adding another reserved word "is" though,
so
I'd like to suggest simply using a ":" ... as in:class A<T : T1> { ... }
In this case I would suggest to use class A<T <: T1> which leaves room
open to define lower bounds later on (either with <: as well or with
:> as
in scala)Consistent with return type-hints, it should feel like home?
For sure nobody wants to type out "instanceof", and (as pointed out
in
the RFC) the instanceof operator checks the type of
an object, which is not what this is doing - a type argument is
not an
"instance of" anything. The ":" is more neutral in that
regard maybe?On Thu, Apr 21, 2016 at 10:32 AM, Josh Di Fabio <
joshdifabio@gmail.com>
wrote:On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other
two.I really don't like 'as' in this context, even if Hack uses it, as
it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other
two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are
approved, then generics would have to support them as well.
Likewise
if this feature becomes approved in generics, it would make sense
to
support them in regular type hints as well.
The RFC makes a reference to generic closures, which may look
something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this
feature
at https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each
other (please correct me if I am wrong).My question about this is would you prefer the generics RFC
exclude
this part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>".
However to ease the implementation it was decided that should be a
separate feature.
So we'd like to find out if everyone else feels the same way about
callable types.
This RFC currently doesn't specify in detail how reflection would
work. We have attempted a few API designs, but due to generic
classes being ...
generic, it is difficult to find a suitable way to glean
information
about a class in a backwards compatible manner. So we will need
some
help on this one.
Aside from these top issues on our own list, however does everyone
feel about the proposal in general?
As the RFC is still in draft, we will continue to make changes to
it
as more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed
a
few important messages that were mailed directly to internals, but
hopefully I've managed to fix that now.--
To unsubscribe,
visit: http://www.php.net/unsub.php--
Guilherme Blanco
Lead Architect at E-Block--
Guilherme Blanco
Lead Architect at E-Block
--
Guilherme Blanco
Lead Architect at E-Block
guilhermeblanco@gmail.com wrote on 29/04/2016 15:26:
You can add subtypes of A to a List<A> in Java. What List<? extends A>
means is that the list itself may be a list of any type, provided that type
is compatible with A. So if B extends A, List<B> is compatible with List<?
extends A>, and when reading items you can assume they will be compatible
with A (since B extends A) but you can't add an A (because it's actually a
list of Bs).Wrong. This is documented here
https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html and
specifically states:To write the method that works on lists of Number and the subtypes of Number,
such as Integer, Double, and Float, you would specify List<? extends
Number>. The term List<Number> is more restrictive than List<? extends
Number> because the former matches a list of type Number only, whereas
the latter matches a list of type Number or any of its subclasses.
Forgive me for butting in if I'm completely wrong, but is the confusion
here between declaring an instance and declaring a parameter constraint?
I think the point is that you can declare variables as follows:
List<Integer> li; // only accepts Integer
List<Double> ld; // only accepts Double
List<Number> ln; // accepts any Number, even if it is in fact an Integer
or Double instance
But if you then define a function accepting a parameter:
public static double sumOfList(List<Number> list)
Now your argument has to be of type List<Number>; you can pass "ln"
above, but not "li" or "ld". To relax the function's contract, you can
write this instead:
public static double sumOfList(List<? extends Number> list)
Now the argument can be a List<Integer> or List<Double>, so "li" and
"ld" are legal arguments.
The reason the default case is to be invariant, rather than covariant,
is explained on the blog post Jesse linked to:
http://hhvm.com/blog/9215/covariance-contravariance-and-super-type-constraints
In a nutshell, consider this function:
function addPiToList(List<Number> $list): void
{
$list->add(3.1415); // for the sake of example, assume 3.1415 instanceOf
Double
}
If we allow covariance, then we can run addPiToList(new List<Integer>)
and the type conditions are met; clearly this is not sane, and is going
to result in an error somewhere.
So we need to be stricter:
function addPiToList(List<Double> $list): void
But the actual contract we want here is "any list we can legally add a
Double to", and if we get a List<Number> or a List<*>, it would work
fine, so our check is too strict. What's needed is a way to declare our
function as contravariant:
function addPiToList(List<? super Double> $list): void
Now we can pass in a List<Double> or a List<Number>, but not a
List<Integer>, and our contract holds nicely.
It took me a while to get my head around, but I think the above makes
sense...
Regards,
Rowan Collins
[IMSoP]
After reading your email again, I find myself agreeing. This is because I
don't consider List<Integer> to be a subtype of List<Number>.
LinkedList<Number> would be a subtype of List<Number>, but inheritance
doesn't really extend any further beyond that, for the reason you have
illustrated.
The example you gave is similar to implementing an interface with a type
hint of Number, but trying to changing it to Integer. The method
signatures are clearly incompatible, so I believe the same applies here in
generics.
guilhermeblanco@gmail.com wrote on 29/04/2016 15:26:
You can add subtypes of A to a List<A> in Java. What List<? extends A>
means is that the list itself may be a list of any type, provided that
type
is compatible with A. So if B extends A, List<B> is compatible with
List<?
extends A>, and when reading items you can assume they will be
compatible
with A (since B extends A) but you can't add an A (because it's
actually a
list of Bs).Wrong. This is documented here
https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html
and
specifically states:To write the method that works on lists of Number and the subtypes of
Number,such as Integer, Double, and Float, you would specify List<? extends
Number>. The term List<Number> is more restrictive than List<? extends
Number> because the former matches a list of type Number only, whereas
the latter matches a list of type Number or any of its subclasses.Forgive me for butting in if I'm completely wrong, but is the confusion
here between declaring an instance and declaring a parameter constraint?I think the point is that you can declare variables as follows:
List<Integer> li; // only accepts Integer
List<Double> ld; // only accepts Double
List<Number> ln; // accepts any Number, even if it is in fact an Integer
or Double instanceBut if you then define a function accepting a parameter:
public static double sumOfList(List<Number> list)
Now your argument has to be of type List<Number>; you can pass "ln" above,
but not "li" or "ld". To relax the function's contract, you can write this
instead:public static double sumOfList(List<? extends Number> list)
Now the argument can be a List<Integer> or List<Double>, so "li" and "ld"
are legal arguments.The reason the default case is to be invariant, rather than covariant, is
explained on the blog post Jesse linked to:
http://hhvm.com/blog/9215/covariance-contravariance-and-super-type-constraintsIn a nutshell, consider this function:
function addPiToList(List<Number> $list): void
{
$list->add(3.1415); // for the sake of example, assume 3.1415 instanceOf
Double
}If we allow covariance, then we can run addPiToList(new List<Integer>) and
the type conditions are met; clearly this is not sane, and is going to
result in an error somewhere.So we need to be stricter:
function addPiToList(List<Double> $list): void
But the actual contract we want here is "any list we can legally add a
Double to", and if we get a List<Number> or a List<*>, it would work fine,
so our check is too strict. What's needed is a way to declare our function
as contravariant:function addPiToList(List<? super Double> $list): void
Now we can pass in a List<Double> or a List<Number>, but not a
List<Integer>, and our contract holds nicely.It took me a while to get my head around, but I think the above makes
sense...Regards,
Rowan Collins
[IMSoP]
Dominic Grostate wrote on 29/04/2016 16:59:
After reading your email again, I find myself agreeing. This is
because I don't consider List<Integer> to be a subtype of List<Number>.LinkedList<Number> would be a subtype of List<Number>, but inheritance
doesn't really extend any further beyond that, for the reason you have
illustrated.
Precisely, the rules for variance of the type parameter <Number> are not
the same as the rules for variance of the generic class List itself. The
exact variance that you want is different in different situations, so
the safest assumption the language can make is to make the type
parameter invariant by default, and then possibly have a syntax for you
to state that you want covariance or contravariance in specific situations.
Regards,
Rowan Collins
[IMSoP]
This means then that to use a subtype for the type argument, it is safe for
the function to READ the argument, but unsafe to WRITE to it.
Conversely. If the function accepted List<Integer>, but was theoretically
given List<Number>, WRITING would be safe, but READING wouldn't.
Given this, it doesn't matter how it's done, both scenarios are prone to
error, and the only options are to either accept that or disallow it.
Dominic Grostate wrote on 29/04/2016 16:59:
After reading your email again, I find myself agreeing. This is because
I don't consider List<Integer> to be a subtype of List<Number>.LinkedList<Number> would be a subtype of List<Number>, but inheritance
doesn't really extend any further beyond that, for the reason you have
illustrated.Precisely, the rules for variance of the type parameter <Number> are not
the same as the rules for variance of the generic class List itself. The
exact variance that you want is different in different situations, so the
safest assumption the language can make is to make the type parameter
invariant by default, and then possibly have a syntax for you to state that
you want covariance or contravariance in specific situations.Regards,
Rowan Collins
[IMSoP]
Dominic Grostate wrote on 29/04/2016 17:45:
This means then that to use a subtype for the type argument, it is
safe for the function to READ the argument, but unsafe to WRITE to it.Conversely. If the function accepted List<Integer>, but was
theoretically given List<Number>, WRITING would be safe, but READING
wouldn't.
Precisely. If you read the blog post linked earlier [1], that's exactly
how they describe it.
Given this, it doesn't matter how it's done, both scenarios are prone
to error, and the only options are to either accept that or disallow it.
The only safe default is to disallow it.
However, for a particular implementation, you as the author may know
that you are only ever going to write to a particular parameter, and can
therefore safely allow that parameter to be "too broad"; or you may know
that you are only ever going to read from it, so can safely allow it to
be "too narrow". This requires extra syntax in the language, and is
fiddly to use right, so should probably be left out of the first
implementation and considered later.
This is exactly what Hack did, adding a class-level annotation of <+T>
or <-T> for read-only or write-only parameters, respectively, to their
existing implementation.
[1]
http://hhvm.com/blog/9215/covariance-contravariance-and-super-type-constraints
Regards,
Rowan Collins
[IMSoP]
On Sat, Apr 30, 2016 at 12:26 AM, guilhermeblanco@gmail.com <
guilhermeblanco@gmail.com> wrote:
Wrong. This is documented here
https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html
and specifically states:To write the method that works on lists of Number and the subtypes of
Number, such as Integer, Double, and Float, you would specify List<?
extends Number>. The term List<Number> is more restrictive than List<?
extends Number> because the former matches a list of type Number only,
whereas the latter matches a list of type Number or any of its
subclasses.
I think that documentation confirms what I wrote, but I guess it depends
how you read it. In any case here is the corresponding Java code, with the
output from javac for the lines that produce errors. You can test it
yourself.
package com.testvariance;
import java.util.LinkedList;
import java.util.List;
class C {
}
class A extends C {
}
class B extends A {
}
public class Main {
public static void main(String[] args) {
// You can add subtypes of A to List<A> in Java
List<A> la = new LinkedList<A>();
la.add(new B());
// So if B extends A, List<B> is compatible with List<? extends A>
List<? extends A> lb = new LinkedList<B>();
// when reading items you can assume they will be compatible with A
(since B extends A)
A a1 = lb.get(0);
// but you can't add an A (because it's actually a list of Bs)
lb.add(new A()); // Error:(25, 14) java: no suitable method found
for add(com.testvariance.A)
// So if A extends C, List<C> is compatible with List<? super A>
List<? super A> lc = new LinkedList<C>();
// and you can add an A (because A is compatible with C)
lc.add(new A());
// but when reading items you can't assume they're going to be As
(because it's actually a list of Cs)
A a2 = lc.get(0); // Error:(33, 28) java: incompatible types:
capture#2 of ? super com.testvariance.A cannot be converted to
com.testvariance.A
}
}
Rasmus is purely following already defined PHP covariance definition that
currently exists for type hinting into generic types.
Wait, where does PHP already define covariance? The only type of generic
I've ever seen in PHP is Foo[] generic array types in PhpDoc (which of
course would be covariant because arrays are pass-by-value, unlike
objects). AFAIK type hinting is presently restricted to plain
classes/interfaces/scalars/array/callable, no generics at all.
Java differs from that and accept covariance for type definitions, but
restricts for generic types.
--
Guilherme Blanco
Lead Architect at E-Block
In this case I would suggest to use class A<T <: T1> which leaves room open to define lower bounds later on
IMHO that is bordering on unreadable - all those brackets are really confusing
and hard on the eyes.
Either way, using : does not prevent us from adding lower bounds later
on - but even
then, upper bound is the 99% use case, so I don't think it makes sense to design
the syntax around a possible future upper bound.
If we do support it in the future, I don't think anyone's going to
care what it looks like,
as it's unlikely most people will ever encounter it or need it.
-----Ursprüngliche Nachricht-----
Von: Rasmus Schultz [mailto:rasmus@mindplay.dk]
Gesendet: Montag, 25. April 2016 18:09
An: Josh Di Fabio
Cc: Dominic Grostate; Guilherme Blanco; Mathieu Rochette; Ben Scholzen 'DASPRiD'; Sara Golemon; PHP internals; Mathieu
Rochette
Betreff: Re: [PHP-DEV] [RFC:generics]I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.I second that.
I hear the concerns about adding another reserved word "is" though, so I'd like to suggest simply using a ":" ... as in:
class A<T : T1> { ... }
In this case I would suggest to use class A<T <: T1> which leaves room open to define lower bounds later on (either with <: as well or with :> as in scala)
Consistent with return type-hints, it should feel like home?
For sure nobody wants to type out "instanceof", and (as pointed out in the RFC) the instanceof operator checks the type of
an object, which is not what this is doing - a type argument is not an "instance of" anything. The ":" is more neutral in that
regard maybe?On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.I really don't like 'as' in this context, even if Hack uses it, as it
doesn't reflect in English terms what the code is doing. As others
have already said, it reads as if 'T' is being aliased to 'Bar'.On Wed, Apr 20, 2016 at 8:17 PM, Dominic Grostate
codekestrel@googlemail.com wrote:Thanks for you're input everyone.
So far, we have read some ideas for handling upper bounds, or
multiple there of.
The preferred keywords appear to be either "as" or "instanceof".class Foo<T as Bar> {}
class Foo<T instanceof Bar> {}We would like to know for sure then if everyone is largely against
the addition of an "is" keyword, in favour of one of the other two.
There is also a desire to include unions and intersections.
Presently though, this feature feels tied in with
https://wiki.php.net/rfc/union_types meaning if union types are
approved, then generics would have to support them as well. Likewise
if this feature becomes approved in generics, it would make sense to
support them in regular type hints as well.
The RFC makes a reference to generic closures, which may look
something like
this:function my_function(callable<Foo, Bar> $func) {
}
However, an RFC already exists which is very similar to this feature
at https://wiki.php.net/rfc/callable-types
As it currently standards these RFCs appear incompatible with each
other (please correct me if I am wrong).My question about this is would you prefer the generics RFC exclude
this part in favour of a separate or later RFC.
Initially the proposal included generic arrays "array<string>".
However to ease the implementation it was decided that should be a separate feature.
So we'd like to find out if everyone else feels the same way about
callable types.
This RFC currently doesn't specify in detail how reflection would
work. We have attempted a few API designs, but due to generic classes being ...
generic, it is difficult to find a suitable way to glean information
about a class in a backwards compatible manner. So we will need some
help on this one.
Aside from these top issues on our own list, however does everyone
feel about the proposal in general?
As the RFC is still in draft, we will continue to make changes to it
as more popular idea pop up, so please continue.Thanks.
PS: I wasn't properly subscribed to the mailing list, so I missed a
few important messages that were mailed directly to internals, but
hopefully I've managed to fix that now.
In this case I would suggest to use class A<T <: T1> which leaves room
open to define lower bounds later onIMHO that is bordering on unreadable - all those brackets are really confusing and hard on the eyes.
I agree, it looks quite ugly :-)
Therefore another suggestion:
class A<T1, T2> [Foo <: T1, T2 <: Bar] {
//....
}
Either way, using : does not prevent us from adding lower bounds later on - but even then, upper bound is the 99% use
case, so I don't think it makes sense to design the syntax around a possible future upper bound.If we do support it in the future, I don't think anyone's going to care what it looks like, as it's unlikely most people will ever
encounter it or need it.
You're probably right. I use wildcard types with lower bounds in Java from time to time but rather rarely.
Hi all,
Am 20.04.2016 um 16:44 schrieb guilhermeblanco@gmail.com:
If I want to hire/move a person to a department that is registered in the
system, but is not a 3rd party company person, how would you achieve that
considering the following class structure?class Person {}
class Employee extends Person {}
class AssociateEmployee extends Employee {}
class Manager extends Employee {}Considering your function:
function assignToDepartment<T>(T $person) : bool;
Generic type "T" in the function prototype needs to accept Person (new
hire), Employee and Manager (transfer), but not AssociateEmployee.
Considering upper bounds support only, your best bet is "T extends Person",
but that would accept AssociateEmployee to be provided, which contradicts
the business rule. Accepting anything lower in the hierarchy prevents new
hires.
That's when lower bounds comes into play. If you define as "T super Manager",
all Person, Employee and Manager gets accepted, but not the
AssociatedEmployee, matching properly the business rule.
I have to strongly disagree with the last sentence here: "T super
Manager" only fulfills your business rule by accident. If you add a
"TeamLeader extends Employee" class, your complete example falls apart
and shows that it is a very bad idea to introduce a "T super X"
requirement. You really want "T implements Person except
AssociateEmployee" so that you can use them like you would use bitmasks.
I think that fundamentally contradicts how interfaces and inheritance
work. The type in the generic should specify which contract (Base
class/interface) you require at least inside the generic class/function.
Everything else is really strange to me. What about interfaces? If
Manager or Employee implement Serializable, will all other classes that
implement Serializable also be allowed here? Please let's not implement
"T super X" kind of generics for use cases that really need something
different.
Thanks,
Dennis
Hi Rasmus
Hello internals,
I'd like to introduce an RFC proposing the addition of generic types and functions:
https://wiki.php.net/rfc/generics
Ben Scholzen started this RFC as a quick draft with a few code samples in August last year, and I have since then worked
with Dominic and Ben towards a more complete, detailed RFC.There are a few holes still, which is why it hasn't moved from Draft to Under Discussion yet, but we feel that it's complete
enough that we can start a discussion about this feature and try to iron out the remaining details.The RFC was previously "unofficially" announced on reddit - this thread generated some good questions and may answer
some of the most immediate questions:https://www.reddit.com/r/PHP/comments/3zx8qs/php_rfcgenerics_update_03_please_comment/
One of the most common criticisms we've heard, is that the syntax would be hard to implement, a few have said
"impossible" - but we feel that, if generics are introduced, it's important that the syntax and features be as familiar as
possible to developers who are experienced with other mainstream web industry languages, such as C# and Java.To that end, Dominic Grostate has worked through most of the tokenizer/parser issues - save for one very exotic edge
case, his fork demonstrates that the proposed syntax can be parsed:https://github.com/orolyn/php-src/commits/generics
Note that this fork is by no means an implementation of generics - it is proof of concept as far as being able to parse the
syntax.We're hoping to find someone, with more experience working on the php codebase, who is willing to collaborate on
further implementation - and we do also have a partial test-suite, defining the big picture expectations for most of the
proposed language features:https://github.com/orolyn/php-src/tree/generics-tests/Zend/tests/generics
We look forward to your comments, questions and (I'm sure) criticisms of this proposal!
Thank You,
Regards,
Rasmus Schultz--
Some questions about bounds checking:
- Example A: why don't you pass T (instead of Computer) to MachineBuilder? Just for the example or is it not possible with your approach?
- What about lower bounds?
In you examples about type checking you write:
var_dump($hat_box instanceof Box); // => (bool) true
Will you support "raw types" similar as in Java, if so why?
you wrote:
TODO: decide whether or not bounded polymorphism should be supported.
I am a bit confused, isn't parametric polymorphism combined with upper bounds already bounded polymorphism?