Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:100275 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 73280 invoked from network); 22 Aug 2017 02:27:31 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 22 Aug 2017 02:27:31 -0000 Authentication-Results: pb1.pair.com smtp.mail=andreas@dqxtech.net; spf=permerror; sender-id=unknown Authentication-Results: pb1.pair.com header.from=andreas@dqxtech.net; sender-id=unknown Received-SPF: error (pb1.pair.com: domain dqxtech.net from 209.85.214.52 cause and error) X-PHP-List-Original-Sender: andreas@dqxtech.net X-Host-Fingerprint: 209.85.214.52 mail-it0-f52.google.com Received: from [209.85.214.52] ([209.85.214.52:36727] helo=mail-it0-f52.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id DA/54-34801-2969B995 for ; Mon, 21 Aug 2017 22:27:30 -0400 Received: by mail-it0-f52.google.com with SMTP id 77so39001442itj.1 for ; Mon, 21 Aug 2017 19:27:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech-net.20150623.gappssmtp.com; s=20150623; h=mime-version:from:date:message-id:subject:to :content-transfer-encoding; bh=MNTVC6ZaruUrj4hXvCG3/yPp5nBvAbyPulc6RXAQb1c=; b=cKpX8mI19b/mnIVIzq7fgCRTLivaZZafnoiyLN52XYKqFVsoFrScq/Ou/znE6FoqNF xwEORBJ/TRlyG1hYrPkqcC5VBitZHoGB+lekXc+DVXdsBbHDT6HTkn18/M0FATpSOzFA NA3H240uQm+/meYTC4unZFCPH1KR6FJAxDMGWoKDcJzJib7jnp6qFyMLfM/KBfG6zJqz 7+9zbu023VXT9JJ5Qll3bTUNwXwnxD1rc89U/jD69htUXfHZ0pA7Jj8quoRpPw5eJpyV Q0iNgraX6SeYQhedzmP8uC2Yirfd08rccU2J3BHXL8o68I91lXyQ9brAx03heWbrxZ27 MiNQ== 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 :content-transfer-encoding; bh=MNTVC6ZaruUrj4hXvCG3/yPp5nBvAbyPulc6RXAQb1c=; b=cyapVqUt+kpw3iND/2jnLV90XrfOr98MgenAU7xEM0valTZsW0XhBlKoALltBXnqmP eCEiweQZ06Y/YgqC/7Qko3jLjQ1/OYPb9FjDmdTecl7HIpQjK9QJ/+zV+8/mFdPczFig Yw8cr/f1zkRFufUHRdUXwtI0HKD/3RRaFK2QySpYeliFGaCpZvp2OQ9CpGmSIvbBJjrc b18OkG+SDVEt+s5Hx0Lctw2tJqxgOiqwky+YYBOLiyu8Va4YIDbKsd7W3Y0JPU9+mWfS qyTODFasS68LWGJFWhoWvSjlEwsP3FWtjOc+CGedh+NmWtPXIti5PzsrBEdgwxPNHGPu WipQ== X-Gm-Message-State: AHYfb5hywb40Ap0ChjvSTl9dZL5rE50g6lxpVT5TsSx7mwH2Sp+CYb4N SGnVRfF4tjdlPb1fNWE= X-Received: by 10.36.162.72 with SMTP id o8mr2213763iti.56.1503368846797; Mon, 21 Aug 2017 19:27:26 -0700 (PDT) Received: from mail-it0-f54.google.com (mail-it0-f54.google.com. [209.85.214.54]) by smtp.googlemail.com with ESMTPSA id g41sm1925782ioj.41.2017.08.21.19.27.26 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 21 Aug 2017 19:27:26 -0700 (PDT) Received: by mail-it0-f54.google.com with SMTP id 77so39001260itj.1 for ; Mon, 21 Aug 2017 19:27:26 -0700 (PDT) X-Received: by 10.36.141.199 with SMTP id w190mr2226607itd.143.1503368840700; Mon, 21 Aug 2017 19:27:20 -0700 (PDT) MIME-Version: 1.0 Received: by 10.50.44.34 with HTTP; Mon, 21 Aug 2017 19:27:00 -0700 (PDT) Date: Tue, 22 Aug 2017 04:27:00 +0200 X-Gmail-Original-Message-ID: Message-ID: To: PHP internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Subject: Contravariance and the "empty type" From: andreas@dqxtech.net (Andreas Hennings) Hello list, for a while I had this thought about contravariance and an "empty type". I don't expect too much of it, for now I just want to share the idea. Maybe this concept even exists somewhere in a different language, and I am not aware of it. I think it has some overlap with generics, https://wiki.php.net/rfc/generic= s. ------------ I think I am not the first one to suggest allowing contravariance for method parameters. E.g. here, "PHP RFC: Parameter Type Widening" https://wiki.php.net/rfc/parameter-no-type-variance From this RFC: > Unfortunately =E2=80=9Ctrue=E2=80=9D contravariance for class types isn't= part of this RFC, as implementing that is far more difficult, and would re= quire additional rules about autoloading and/or class compilation, which mi= ght only be acceptable at a major release. For anyone not familiar with the term: interface I { function foo(J $arg); } interface J extends I { function foo(I $arg); } So: While return types in a child method should be either the same or more narrow, the parameter types should be either the same or more permissive. Without this it would break Liskov substitution. --------------- Now for my actual proposal: The "empty type". We can think of a type (class/interface or primitive) as a set or a constraint on the kind of values that it allows. There is a special type, "mixed", which allows all values. We could also think of it as the union of all types. A natural extension of this concept, on the other end, would be a type "nothing" or "empty", which would allow no values at all. We could think of this as the intersection of all types. In fact it is already sufficient to intersect just two distinct primitive types to get this empty type: "All values that are at the same time string and integer" clearly is an empty type. How would this ever be useful? If we write a base class or interface for a category of interfaces that have a similar signature. interface Fruit {..} interface Apple extends Fruit {..} interface Banana extends Fruit {..} interface AbstractFruitEater { function eat(EMPTY_TYPE $fruit); } interface BananaEater extends AbstractFoodEater { function eat(Banana $banana); } interface AppleEater extends AbstractFoodEater { function eat(Apple $apple); } One could imagine a component that has a list of AbstractFruitEater objects, and chooses one that is suitable for the given fruit, using instanceof. I think the correct term is "chain of responsibility". function eatApple(array $fruitEaters, Apple $apple) { foreach ($fruitEaters as $eater) { if ($eater instanceof AppleEater) { $eater->eat($apple); break; } } } -------------------- We can go one step further. The natural parameter type to use for param $fruit in AbstractFruitEater::foo() would not be the global EMPTY_TYPE, but something more specific: The projected intersection of all real and hypothetical children of interface Fruit. Obviously this does not and cannot exist as a class or interface. Practically, for the values it allows, this is the same as the global EMPTY_TYPE. But unlike the EMPTY_TYPE, this would poses a restriction on the parameter type in child interfaces. What would be the syntax / notation for such a projected hypothetical subty= pe? I don't know. Let's say INTERSECT_CHILDREN So, would the following work? interface Food {..} interface Fruit extends Food {..} interface Banana extends Fruit {..} interface AbstractFoodEater { function eat(INTERSECT_CHILDREN $food); } interface AbstractFruitEater extends AbstractFoodEater { function eat(INTERSECT_CHILDREN $fruit); } interface BananaEater extends AbstractFruitEater { function eat(Banana $banana); } I'm not sure. Liskov would not care. Both AbstractFoodEater and AbstractFruitEater are useless on their own. Maybe there are other logical conflicts which I don't see. ---------- Obviously with generics this base interface would no longer be relevant. https://wiki.php.net/rfc/generics interface FruitEater { function eat(FruitType $fruit); } // This is not really necessary. interface BananaEater extends FruitEater { function eat(Banana $banana); } So, would the "empty type" become obsolete? Maybe. I did not arrive at a final conclusion yet. It still seems too interesting to let it go. -- Andreas