Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:107093 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 66817 invoked from network); 14 Sep 2019 22:53:59 -0000 Received: from unknown (HELO php-smtp3.php.net) (208.43.231.12) by pb1.pair.com with SMTP; 14 Sep 2019 22:53:59 -0000 Received: from php-smtp3.php.net (localhost [127.0.0.1]) by php-smtp3.php.net (Postfix) with ESMTP id 7231F2CE19C for ; Sat, 14 Sep 2019 13:30:27 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp3.php.net X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_LOW,SPF_HELO_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS11403 66.111.4.0/24 X-Spam-Virus: No Received: from out2-smtp.messagingengine.com (out2-smtp.messagingengine.com [66.111.4.26]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp3.php.net (Postfix) with ESMTPS for ; Sat, 14 Sep 2019 13:30:26 -0700 (PDT) Received: from compute7.internal (compute7.nyi.internal [10.202.2.47]) by mailout.nyi.internal (Postfix) with ESMTP id 5E05B21785 for ; Sat, 14 Sep 2019 16:30:26 -0400 (EDT) Received: from imap26 ([10.202.2.76]) by compute7.internal (MEProxy); Sat, 14 Sep 2019 16:30:26 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; bh=CX/uey k6Ra4rs58fYtuoBe4dIF1hMeu1Y1q8jcDDWH8=; b=Kbim8AiazCQGXMow7tzx1V mJsundeNi9ySJm8YJluR2sD8ooQ+XZKU7HpS5mwnczmqiLQE/W+QRZGnKJvewvSf tl11Xk3BOWMpfVAgSPHZmehcQkuact2fivdovmP26CTPovNCjCnxIox7ue1hKRUG pDpX1vTxhkPANWMsX/pasWnFuwrWdUckjgFHD/jw1HNXTFChWbIalkq1y4xHg9s4 Ul131ULHFkilzCmJMr6RMH8tlyxxwAHBK9yk+y5tXKy6cBrGalOy05xJxurnXZTH 30nfnL+YTGkNd4/hKJi2LJmXo1AKSgGP/uRmxxXJ2QFPa79gxwxu1UoFwjJ6ZQNw == X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedufedrtdelgdduheduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepofgfggfkjghffffhvffutgesthdtredtreertdenucfhrhhomhepfdfnrghr rhihucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtoh hmqeenucffohhmrghinhepshhtvggvmhhithdrtghomhenucfrrghrrghmpehmrghilhhf rhhomheplhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhmnecuvehluhhsthgvrh fuihiivgeptd X-ME-Proxy: Received: by mailuser.nyi.internal (Postfix, from userid 501) id A7F1614200A1; Sat, 14 Sep 2019 16:30:25 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.1.7-237-gf35468d-fmstable-20190912v1 Mime-Version: 1.0 Message-ID: <9a52c45d-f4f9-40e5-8919-e5931a64700f@www.fastmail.com> In-Reply-To: References: Date: Sat, 14 Sep 2019 15:29:29 -0500 To: "Steffen via internals" Content-Type: text/plain X-Envelope-From: Subject: Re: [PHP-DEV] [RFC] Object Initializer From: larry@garfieldtech.com ("Larry Garfield") On Fri, Sep 13, 2019, at 9:35 AM, Rowan Tommins wrote: > Hi Rasmus, > > On Fri, 13 Sep 2019 at 11:18, Rasmus Schultz wrote: > > > All in all, I find this feature is useful or applicable only to a few, > > select patterns within the language - it isn't general enough. > > > > > I've trimmed the quote for readability, but agree with basically everything > in this message. :) > > I like the reasoning behind this RFC, but think it is unnecessarily > limited. It's not about "public properties are bad", it's just that if we > can come up with a feature that works for public properties *and* other > programming styles, we should prefer that. > > A related proposal that's come up before is short-hand constructors: > > class Foo { > private int $foo; > public function __construct($this->foo) { > // automatically assigns first parameter to $this->foo so body is > simplified > } > } > > Combine that with opt-in named parameters, and the Customer example from > the RFC might look something like this: > > class Customer > { > private $id; > private $name; > private DateTimeImmutable $createdAt; > > public function __construct(id => $this->id, name => $this->name, > createdDate => string $dateString = 'now') > { > $this->createdAt = new DateTimeImmutable($dateString); > } > }$customer = new Customer( > id => 123, > name => 'John Doe', > createdDate => '2019-01-01 12:34' > ); > > > It's slightly more verbose, but a lot more flexible. > > > As a side note, I have always thought stdClass was a bit of a kludge, and > now we have real anonymous classes I would love to see it gradually phased > out. I would much rather see syntax for capturing variables in an anonymous > class declaration than new ways to create stdClass objects. > > Regards, > -- > Rowan Tommins > [IMSoP] I'm a big fan of using defined classes over anon arrays for struct-like data, for various reasons (cf https://steemit.com/php/@crell/php-use-associative-arrays-basically-never), so I'm sympathetic toward anything that makes that easier. However, I have to agree with Rasmus and Rowan that the current RFC proposal is too "narrow" to solve that effectively. The problem to be solved is that this: class Employee { public int $age; public string $name; public ?Employee $boss; public function __construct(int $age, string $name, Employee $boss = null) { $this->age = $age; $this->name = $name; $this->boss = $boss; } } $e = new Employee(34, "Bob", $sally); // or $e = new Employee(); $e->age = 34; $e->name = 'Bob'; $e->boss = $sally; Is just annoyingly verbose and annoying to work with. I agree. However, initializers as presented solve only a subset of this problem space: The case where: * The properties are public. * The constructor can be viable with no parameters. * There is no needed validation, or we can expect a user to manually call validate() or similar afterward. While that case absolutely exists, it is only a subset of the relevant use cases. As Rasmus and Rowan note, however, breaking the problem apart into two pieces would allow it to handle a wider array of use cases while still solving the one presented. For example (and with no thought given to syntax here, 'hoist' is almost certainly the wrong word): class Employee { protected int $age; protected string $name; protected ?Employee $boss; public function __construct({hoist int $age, hoist string $name, hoist Employee $boss = null}) { if ($age < 18) throw new ChildLaborException(); } } $e = new Employee({ age = 34, name = 'Bob', boss = $sally, }); Solves the initializer use case with very similar syntax for the caller, BUT also works for: * Any function where named parameters would be useful, independent of object initialization. * Any object where the properties in question are not public. * Any object where validation is required and you don't trust the caller to remember to call it (which you shouldn't trust) * Any object where only some of the constructor parameters map to object properties, a fact that should be hidden from the caller. And probably other situations. I could envision taking it a step further and writing instead: class Employee { public function __construct({hoist private int $age, hoist protected string $name, hoist public ?Employee $boss = null}) { if ($age < 18) throw new ChildLaborException(); } } And now you have even less typing and repetition. If you wanted to go really really far, you could even: class Employee { protected int $age; protected string $name; protected ?Employee $boss; public function hoist __construct() { if ($age < 18) throw new ChildLaborException(); } } As a short hand that enables any property to be passed in, but only using a named call, and still enforce that some parameters are required. I could see that being very useful for service objects, as well as value objects, where there is almost always a 1:1 mappnig from properties to constructor parameters. That is, named arguments are useful on their own (and have been discussed before). Auto-populating properties from the constructor is useful on its own. With the combination of those, the need for a specialized initializer syntax goes away, because they are an emergent property of the other two features. So on net, we get more power and more flexibility for less new syntax. Implementing a more limited syntax (just initialization for a subset of use cases) makes implementing the more robust approach in the future harder, because we'd have to be super careful to avoid BC issues or else end up with two slightly different syntaxes for slightly different but really the same thing behaviors; that's not only bad for usability and learnability but we're running out of convenient sigils. :-) So I would be -1 on initializers as proposed, but +1 on addressing that problem space through the more robust combination of named arguments and auto-populating properties. --Larry Garfield