I propose the following language additions:
- add three new keywords to the available syntax for class declarations:
'inherits' and 'subtypes' and 'patterns'.
Currently the 'extends' keyword means both inherits and subtypes,
which leads to unnecessarily restrictive typing problems in order to
satisfy the Liskov substitution principle. 'Inherits' would mean that
class A inherits B's properties and methods but is not a subtype of B and
cannot be substituted for B. And just as A can override/overload B's
methods, the method signatures would not be subject to the parameter
widening / return-type restricting principle necessary for substitution.
Conversely, the 'subtypes' keyword indicates that if A subtypes B then A
can be substituted anywhere B can be used but 'subtypes' does not imply
that A inherits any of B's methods and properties - simply that it has
signatures that conform to the parameter widening and return-type
restricting principles of substitution. Lastly 'patterns' complements the
'implements' keyword. A class 'patterns' an interface if it conforms to
the declared signature of the interface but it does not imply that the
concrete class can be substituted for any other object which either
implements or patterns the interface.
It should be possible to write 'A inherits, subtypes B' and this should
have the same effect as 'A extends B'.
- Add two new operators: 'descendentof' and 'subtypeof' that would augment
'instanceof'. Semantics parallel the class declaration keywords described
above.
Benefits:
It would then be possible to write something like the following:
interface ValidatorInterface
{
public function validate($x) : bool;
public function getErrmsg() : string;
}
abstract class Validator patterns ValidatorInterface
{
abstract public function validate($x) : bool;
public function getErrmsg() : string
{
return $this->errmsg;
}
}
class ValidatorNonNegativeInteger inherits Validator patterns
ValidatorInterface
{
public function validate(int $x) : bool { return 0 <= $x; }
}
class ValidatorAlphaText(string $x) inherits Validator patterns
ValidatorInterface : bool
{
return 1 == preg_match('/^[a-zA-Z]+$/', $x);
}
ValidatorNonNegativeInteger and ValidatorAlphaText cannot be substituted
for each other or Validator or ValidatorInterface. Explicitly trying to
upcast ValidatorNonNegativeInteger should produce a compiler error (e.g.
(Validator) new ValidatorNonNegativeInteger() is not legal).
If you adopt the data type 'mixed' and use it as in "public function
validate(mixed $x)", it makes it even clearer that the syntax permits
covariant parameters.
Currently the compiler allows contradictory parameter declaration between
parent and descendant (using 'extends') but produces a runtime error
indicating that the child method's parameter must conform to that of the
parent. Because substitution is not an issue when A inherits B (using
‘inherits’ as above, not ‘extends’), a parent declaring "function foo(int
$bar)" and a descendant declaring "function foo(string $bar)" has no
theoretical problem as far as I can tell and should not produce an error.
Along with generics (already being suggested / discussed I think - and
thank you for unions), this approach should lead to DRYer code and cleaner
abstractions. In the example above, ValidatorNonNegativeInteger and
ValidatorAlphaText can leverage common code in the parent class. And it is
not necessary to write two separate interfaces in order to handle the two
different data types. Essentially, it creates a bridge between the old
'untyped' Zvals and the tightly typed C derivatives.
Who Is Going To Write This?
I am relatively new to the PHP community and have never once glanced at the
internals of PHP. As much as I would love to do it, my learning curve will
be quite some time before I would be ready to tackle something like this.
And I think the time for this idea is now while there is so much momentum
in the community on type-safety. I am considering getting involved in the
community per the guidance (test writing, documentation, etc) but have not
plucked up the courage to make the commitment yet. And my C is super
rusty. If this falls on deaf ears, I am afraid the idea will never bear
fruit. If everyone thinks it is a good idea, then I am particularly sorry I
cannot do it myself since it is wrong to make my problem someone else’s
problem. I would consider writing something in PHP to implement the idea,
but cannot really see how it could be accomplished.
Thank you for your time and consideration. And thank you so much for all
the work you do to make PHP the product that it is.
Kind regards,
Doug Wilbourne
I propose the following language additions:
- add three new keywords to the available syntax for class declarations:
'inherits' and 'subtypes' and 'patterns'.Currently the 'extends' keyword means both inherits and subtypes,
which leads to unnecessarily restrictive typing problems in order to
satisfy the Liskov substitution principle. 'Inherits' would mean that
class A inherits B's properties and methods but is not a subtype of B and
cannot be substituted for B. And just as A can override/overload B's
methods, the method signatures would not be subject to the parameter
widening / return-type restricting principle necessary for substitution.
Conversely, the 'subtypes' keyword indicates that if A subtypes B then A
can be substituted anywhere B can be used but 'subtypes' does not imply
that A inherits any of B's methods and properties - simply that it has
signatures that conform to the parameter widening and return-type
restricting principles of substitution. Lastly 'patterns' complements the
'implements' keyword. A class 'patterns' an interface if it conforms to
the declared signature of the interface but it does not imply that the
concrete class can be substituted for any other object which either
implements or patterns the interface.It should be possible to write 'A inherits, subtypes B' and this should
have the same effect as 'A extends B'.
- Add two new operators: 'descendentof' and 'subtypeof' that would augment
'instanceof'. Semantics parallel the class declaration keywords described
above.Benefits:
It would then be possible to write something like the following:
interface ValidatorInterface
{
public function validate($x) : bool; public function getErrmsg() : string;
}
abstract class Validator patterns ValidatorInterface
{
abstract public function validate($x) : bool; public function getErrmsg() : string { return $this->errmsg; }
}
class ValidatorNonNegativeInteger inherits Validator patterns
ValidatorInterface{
public function validate(int $x) : bool { return 0 <= $x; }
}
class ValidatorAlphaText(string $x) inherits Validator patterns
ValidatorInterface : bool{
return 1 == preg_match('/^[a-zA-Z]+$/', $x);
}
ValidatorNonNegativeInteger and ValidatorAlphaText cannot be substituted
for each other or Validator or ValidatorInterface. Explicitly trying to
upcast ValidatorNonNegativeInteger should produce a compiler error (e.g.
(Validator) new ValidatorNonNegativeInteger() is not legal).If you adopt the data type 'mixed' and use it as in "public function
validate(mixed $x)", it makes it even clearer that the syntax permits
covariant parameters.Currently the compiler allows contradictory parameter declaration between
parent and descendant (using 'extends') but produces a runtime error
indicating that the child method's parameter must conform to that of the
parent. Because substitution is not an issue when A inherits B (using
‘inherits’ as above, not ‘extends’), a parent declaring "function foo(int
$bar)" and a descendant declaring "function foo(string $bar)" has no
theoretical problem as far as I can tell and should not produce an error.Along with generics (already being suggested / discussed I think - and
thank you for unions), this approach should lead to DRYer code and cleaner
abstractions. In the example above, ValidatorNonNegativeInteger and
ValidatorAlphaText can leverage common code in the parent class. And it is
not necessary to write two separate interfaces in order to handle the two
different data types. Essentially, it creates a bridge between the old
'untyped' Zvals and the tightly typed C derivatives.Who Is Going To Write This?
I am relatively new to the PHP community and have never once glanced at the
internals of PHP. As much as I would love to do it, my learning curve will
be quite some time before I would be ready to tackle something like this.
And I think the time for this idea is now while there is so much momentum
in the community on type-safety. I am considering getting involved in the
community per the guidance (test writing, documentation, etc) but have not
plucked up the courage to make the commitment yet. And my C is super
rusty. If this falls on deaf ears, I am afraid the idea will never bear
fruit. If everyone thinks it is a good idea, then I am particularly sorry I
cannot do it myself since it is wrong to make my problem someone else’s
problem. I would consider writing something in PHP to implement the idea,
but cannot really see how it could be accomplished.Thank you for your time and consideration. And thank you so much for all
the work you do to make PHP the product that it is.Kind regards,
Doug Wilbourne
Hi Doug. Thank you for your interest in PHP.
I generally agree with the issues of combining subtype and uses-code-from into a single operation; PHP already has a solution to that, though: Traits. It's not exactly the same as what you describe, but it's close enough for most cases.
cf: https://www.garfieldtech.com/blog/beyond-abstract
That aside, a change of that scale couldn't happen in PHP 8 at this point. Feature freeze is in less than 2 months and even just figuring out how to implement such a change would take longer than that, even assuming universal buy in.
--Larry Garfield
PHP's notion of "implements" is identical to that of Java and C#. This is
not by accident, many programmers have to work in multiple programming
languages. The less a language does things "differently" the better.
And honestly, the upsides of this proposal are not worth the BC breaks
created with the new keywords.
On Wed, Jun 10, 2020 at 11:16 AM Doug Wilbourne dougwilbourne@gmail.com
wrote:
I propose the following language additions:
- add three new keywords to the available syntax for class declarations:
'inherits' and 'subtypes' and 'patterns'.Currently the 'extends' keyword means both inherits and subtypes,
which leads to unnecessarily restrictive typing problems in order to
satisfy the Liskov substitution principle. 'Inherits' would mean that
class A inherits B's properties and methods but is not a subtype of B and
cannot be substituted for B. And just as A can override/overload B's
methods, the method signatures would not be subject to the parameter
widening / return-type restricting principle necessary for substitution.
Conversely, the 'subtypes' keyword indicates that if A subtypes B then A
can be substituted anywhere B can be used but 'subtypes' does not imply
that A inherits any of B's methods and properties - simply that it has
signatures that conform to the parameter widening and return-type
restricting principles of substitution. Lastly 'patterns' complements the
'implements' keyword. A class 'patterns' an interface if it conforms to
the declared signature of the interface but it does not imply that the
concrete class can be substituted for any other object which either
implements or patterns the interface.It should be possible to write 'A inherits, subtypes B' and this should
have the same effect as 'A extends B'.
- Add two new operators: 'descendentof' and 'subtypeof' that would augment
'instanceof'. Semantics parallel the class declaration keywords described
above.Benefits:
It would then be possible to write something like the following:
interface ValidatorInterface
{
public function validate($x) : bool; public function getErrmsg() : string;
}
abstract class Validator patterns ValidatorInterface
{
abstract public function validate($x) : bool; public function getErrmsg() : string { return $this->errmsg; }
}
class ValidatorNonNegativeInteger inherits Validator patterns
ValidatorInterface{
public function validate(int $x) : bool { return 0 <= $x; }
}
class ValidatorAlphaText(string $x) inherits Validator patterns
ValidatorInterface : bool{
return 1 == preg_match('/^[a-zA-Z]+$/', $x);
}
ValidatorNonNegativeInteger and ValidatorAlphaText cannot be substituted
for each other or Validator or ValidatorInterface. Explicitly trying to
upcast ValidatorNonNegativeInteger should produce a compiler error (e.g.
(Validator) new ValidatorNonNegativeInteger() is not legal).If you adopt the data type 'mixed' and use it as in "public function
validate(mixed $x)", it makes it even clearer that the syntax permits
covariant parameters.Currently the compiler allows contradictory parameter declaration between
parent and descendant (using 'extends') but produces a runtime error
indicating that the child method's parameter must conform to that of the
parent. Because substitution is not an issue when A inherits B (using
‘inherits’ as above, not ‘extends’), a parent declaring "function foo(int
$bar)" and a descendant declaring "function foo(string $bar)" has no
theoretical problem as far as I can tell and should not produce an error.Along with generics (already being suggested / discussed I think - and
thank you for unions), this approach should lead to DRYer code and cleaner
abstractions. In the example above, ValidatorNonNegativeInteger and
ValidatorAlphaText can leverage common code in the parent class. And it is
not necessary to write two separate interfaces in order to handle the two
different data types. Essentially, it creates a bridge between the old
'untyped' Zvals and the tightly typed C derivatives.Who Is Going To Write This?
I am relatively new to the PHP community and have never once glanced at the
internals of PHP. As much as I would love to do it, my learning curve will
be quite some time before I would be ready to tackle something like this.
And I think the time for this idea is now while there is so much momentum
in the community on type-safety. I am considering getting involved in the
community per the guidance (test writing, documentation, etc) but have not
plucked up the courage to make the commitment yet. And my C is super
rusty. If this falls on deaf ears, I am afraid the idea will never bear
fruit. If everyone thinks it is a good idea, then I am particularly sorry I
cannot do it myself since it is wrong to make my problem someone else’s
problem. I would consider writing something in PHP to implement the idea,
but cannot really see how it could be accomplished.Thank you for your time and consideration. And thank you so much for all
the work you do to make PHP the product that it is.Kind regards,
Doug Wilbourne