I propose the following language additions:


1) 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'.



2) 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

Reply via email to