Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:110464 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 65454 invoked from network); 10 Jun 2020 17:32:31 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 10 Jun 2020 17:32:31 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id C591118055C for ; Wed, 10 Jun 2020 09:16:27 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 10 Jun 2020 09:16:27 -0700 (PDT) Received: by mail-wr1-f43.google.com with SMTP id y17so2921894wrn.11 for ; Wed, 10 Jun 2020 09:16:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=N3DCOX+1JozHTx8qO91W2rXJlfZKvhFBkkP6AqTCE8c=; b=uhuLI/u0aDMXyUp6sgcCOeg2uL9ndVc+XWEAsSg2AFa+3WSf7PFiz+dfFDYbsGCLcV q2beUBiwhhvLEImD1jfYKx6pGz7I7vyC/zh4ykjgZHnl78Ix//S0B7E0RkQKkXHaHgul rzYyXlfHqZGkoS7W0AR+ZkAcbAZZ4DW9+Ukm45TXVhHwCuQ5LcUy/BE/HDGgZBL1gEJ0 UkQQpmRs29d5r2U3j5jMBBY8DNOgs2rieK5/yZWWupGCIYTAJODRyiSUsOL4uZzJDhz2 iqiAhVG7u3ZsWf+qaOVj5RYEs0JWuIaA4hM1Q7+V6+zo+DiGm5QE50ss7RsoVXDuNRf8 052w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=N3DCOX+1JozHTx8qO91W2rXJlfZKvhFBkkP6AqTCE8c=; b=eTCjq7+Cw85aDTXLQsWWtLLZbKuRjQgf8yHHN7je1WO3f+O5dilr/FJOyl2OJDfCWl mDByqSlSfHKY7r+mOW6b5ZIPWzcWvDdDcHh3EuEeOCk9+dfu95Estwmw/ZHE5ajVtw/X OtV+BqZhhG/5hJD6dTDZZSeLRHzYt3k/YFTu06Cme9AJZmqUm8L9hXxlsvDsja95+uIa wfOBSgcbPYmx4QAwvdFtzTBVIETG1F3s+qTApJRyb7hTXNtFXu8NG6zMuJdzPxjKI1hB I8TSo01FkkwAH2rohyZOmEHxPEouP5xvFKJ24QzKVBkXiGvT9g6A5UWy53b8mmN+V31f VtZw== X-Gm-Message-State: AOAM530vz+QDAqnZYhQwFk2UgaB/dATGeTuIventqpRDZxdzjQxsPmyF AzSH60jT46oesWKUa0HNB++hC0n1uisiBVDWqivvAlugZxA= X-Google-Smtp-Source: ABdhPJwop+hcXQ0KjIucdpy6b9VSwhKVMZ1IwBAY1aReBnNppVfNF9SrlOBjc46a16b+0xe+GXqgWtbQy0rCxWBLzBA= X-Received: by 2002:adf:9544:: with SMTP id 62mr4494014wrs.32.1591805782729; Wed, 10 Jun 2020 09:16:22 -0700 (PDT) MIME-Version: 1.0 Date: Wed, 10 Jun 2020 12:16:12 -0400 Message-ID: To: internals@lists.php.net Content-Type: multipart/alternative; boundary="0000000000003b292c05a7bd2a4d" Subject: RFC: separate inheritance from subtyping (PHP v8) From: dougwilbourne@gmail.com (Doug Wilbourne) --0000000000003b292c05a7bd2a4d Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable 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 <=3D $x; } } class ValidatorAlphaText(string $x) inherits Validator patterns ValidatorInterface : bool { return 1 =3D=3D 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 =E2=80=98inherits=E2=80=99 as above, not =E2=80=98extends=E2=80=99), a pare= nt 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=E2=80= =99s 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 --0000000000003b292c05a7bd2a4d--