Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:107085 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 32994 invoked from network); 14 Sep 2019 11:13:25 -0000 Received: from unknown (HELO php-smtp3.php.net) (208.43.231.12) by pb1.pair.com with SMTP; 14 Sep 2019 11:13:25 -0000 Received: from php-smtp3.php.net (localhost [127.0.0.1]) by php-smtp3.php.net (Postfix) with ESMTP id 1ECAD2CC929 for ; Sat, 14 Sep 2019 01:49:44 -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=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS3215 2.6.0.0/16 X-Spam-Virus: No Received: from mail-pf1-x42c.google.com (mail-pf1-x42c.google.com [IPv6:2607:f8b0:4864:20::42c]) (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 01:49:43 -0700 (PDT) Received: by mail-pf1-x42c.google.com with SMTP id a2so536689pfo.10 for ; Sat, 14 Sep 2019 01:49:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=newclarity-net.20150623.gappssmtp.com; s=20150623; h=from:message-id:mime-version:subject:date:in-reply-to:cc:to :references; bh=z2fFL9iL3HbraiW2fjXHyQ9vFoRz4ZOYyWFq4SWLdpA=; b=wbZsdyOD1KJlIqoKGjm1/g7ozim429i39XxGUtMgWiv6fAfwjPoDg399ssRMB61zS6 JIkQ/XNeqfXhx4ck4vP80ZxjMXFcxqrspV4nSyOaOc5iASu13hhjm4xCoCgkdCnyPw5H iDaz8G24KJq7oImvKPjb/Gq4HHS0+uJva/mXUcTIxZ6E529rYm3vASsxFT2dZ6sJKwl/ s/8jKX+a6/Lm25R+EBs/1aRUKbYFyLnhffZ1gUAoIQhepCEzJk3P5KX+xNW50rrgmtpL YVnooal2DtZQ7w7XidWXh0TG64c5LrVhEL9kELDf6Nnd3YTV8hbzt3BXVsByKTyp6R16 AFJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:message-id:mime-version:subject:date :in-reply-to:cc:to:references; bh=z2fFL9iL3HbraiW2fjXHyQ9vFoRz4ZOYyWFq4SWLdpA=; b=efnAgs2xkHi481t/FRDcLoseIwOCgC6h1G7D+4K/kUArY+eEfHc57p5WKb0B2NnPKj Xr+YZdzWoBqOPSpBopCTxay70MfZfbIuW/ZDhu7RiBesUh3uz/4KJnPhvdq+YtMuUb20 hXWa2myOCv0uxhwc5Di/l1eYNyOzrfeTiooQiFxWUKNfBd6qKdWv0tSvZHy4CE19fQ6R w86R95U//nQL+XmmmA5P9J87t1qLaVS/F+KTzxcsNrVeehQ2/t0fWLVyCkCgaaDrNkEO u38uTl2s2Z3YlGMDTEK2ORCflYsa/AAGJdrni91brYVId6ILZIPorlfhckP4nG5dbuRN jkqg== X-Gm-Message-State: APjAAAWzSldg59FTV82vzez8g+6lzI0+ED53NGbdJKfZ0QJiXmA9HVC5 5wSVLqp5XJfzKZT0yihAzDHrtj6Ek6EiGg== X-Google-Smtp-Source: APXvYqx6II3BjaHlnxehAng6/Jvvq0c4vIbZiBodc8R4PXAJtBVLX8QiSnXLnejfxCZgofZjtGEpLQ== X-Received: by 2002:a65:62d1:: with SMTP id m17mr49189459pgv.271.1568450982566; Sat, 14 Sep 2019 01:49:42 -0700 (PDT) Received: from [10.20.3.119] ([198.233.189.11]) by smtp.gmail.com with ESMTPSA id g1sm26099508pgg.27.2019.09.14.01.49.40 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 14 Sep 2019 01:49:41 -0700 (PDT) Message-ID: Content-Type: multipart/alternative; boundary="Apple-Mail=_904DEA3E-1881-4879-B2BD-E202E35057B6" Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\)) Date: Sat, 14 Sep 2019 01:49:39 -0700 In-Reply-To: <0275793C-EC66-4693-B74F-B794E6FF0AC6@newclarity.net> Cc: =?utf-8?Q?Micha=C5=82_Brzuchalski?= , PHP Internals List To: Rasmus Schultz References: <0275793C-EC66-4693-B74F-B794E6FF0AC6@newclarity.net> X-Mailer: Apple Mail (2.3445.104.11) X-Envelope-From: Subject: Re: [PHP-DEV] [RFC] Object Initializer From: mike@newclarity.net (Mike Schinkel) --Apple-Mail=_904DEA3E-1881-4879-B2BD-E202E35057B6 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On Sep 13, 2019, at 3:18 AM, Rasmus Schultz > wrote: >=20 > 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. > ... > In my opinion, language features should be as general as possible > ... > My strong preference over this feature would be named parameters,=20 > ... > It works for models with private/protected fields, classes with = accessors, > classes with validations in constructors or factory-methods, and so = on. >=20 > In other words, it works more generally with all common patterns and > practices - in many ways, it just seems like a better fit for the = language. I mostly agree with the first statements, in abstract principle. =20 But ironically I come to the exact opposite conclusion that your latter = statements imply, i.e. that named parameters are not a generalizable = enough feature and that object initializers are much more generalizable. Let me explain where I see limitations of named parameters: 1. Nested structures: Named parameters, assuming they are not shorthand = for initializing objects could not support nested parameters, or at = least not without a lot of other supporting additions to PHP too. $car =3D new Car({=20 yearOfProduction =3D> 1975,=20 vin =3D> "12345678",=20 engine =3D> { type =3D> "v8",=20 capacity =3D> "270ci",=20 }, }) 2. Local usage: Consider the following construct. I might use this above = the top of a loop if I were writing a very add-hoc custom report. = (Please note that I embellished by adding types in a manner I would = prefer here.)=20 The following example shows anonymous class declared in local scope. = The reason we want to initialize a anonymous class is the same reason we = wanted anonymous classes in the first place; often a class is just not = important enough to assign a name.=20 And since naming is "one of the 2 or 3 hardest things in programming" = not having to assign it a name if a name is not needed is a real plus. = And this is not a use-case named parameters address, so this is an = explicit example of where object initializers are more general purpose = than named parameters: $stats =3D new class { int total =3D> 0, int mean =3D> 0, int average =3D> 0, int median =3D> 0, } I won't elaborate on how this class is used since hopefully that code = would be obvious .=20 But I will say that I would use these types of object initializers = almost everywhere I previously have used arrays =E2=80=94 and where = others have used arrays in code I am refactoring. That would reduce = most of array use-cases to collections as I would no longer really need = them as associative arrays, unless and framework or library required me = to use them.=20 If we had the language features to allow us to concisely move = locally-declared associative array code to using anonymous class object = instances then IDEs would be able to validate proper use of class = properties and cut down on an entire class of coding errors. =20 (Yes, theoretically we can already use anonymous classes instead of = arrays but declaring the classes and initializing their properties is so = tedious that almost nobody ever codes that way in PHP. Or at least none = of the code I have ever come across on GitHub or in client's projects.) 3. Passing to subroutines. This is a contrived example, but it is = emblematic of code I write daily.=20 The unfortunate aspect is that since I use an array of $args I cannot = have my IDE nor PHP validate that I used the proper array keys. (Note that my example has an error; I used 'field' with query() instead = of 'fields' as I should have, to illustrate the problem): class Join { public string $table; public string $from; public string $to; } class QueryBuilder { function query(array $args):object[] { if ( ! $this->validate($args) ) { throw new Exception("Dude!"); } $query =3D $this->build($args); return $this->run($query); } function validate(array $args) { ... } function build(array $args) { ... } function run(string $query ) { ... } } $qb =3D new QueryBuilder(); $rows =3D $qb->query(array( field =3D> ['id','name','cost'], table =3D> 'products', where =3D> 'id=3D' . $productId, )); Now let us take a look at this example using named parameters. Hopefully = you can see it is painfully verbose, definitely not DRY, and likely to = result in typos or other refactoring errors:=20 class QueryBuilder { function query(string[] $fields, string $table, Join[] $joins, string[] = $wheres ):object[] { if ( ! $this->validate($fields, $table, $joins, $wheres ) ) { throw new Exception("Dude!"); } $query =3D $this->build($fields, $table, $joins, $wheres); return $this->run($query); =20 } function validate(string[] $fields, string $table, Join[] $joins, = string[] $wheres ) { ... } function build(string[] $fields, string $table, Join[] $joins, = string[] $wheres ) { ... } ... } If we instead use object initializers, it becomes much readable and = maintainable, even though the developer may have initially written the = query() method by using named parameters: And unlike using an array to contain all the values my IDE, PHP and = other tools can validate whether or not I used the correct property = names for Query when calling $qb->query(): class Query { public string[] $fields; public string $table;=20 public Join $join;=20 public string[] $where;=20 } class QueryBuilder { function query(Query $query):object[] { if ( ! $this->validate($query) ) { throw new Exception("Dude!"); } $query =3D $this->build($query); return $this->run($query); } function validate(Query $query) { ... } function build(Query $query) { ... } ... } $qb =3D new QueryBuilder(); $rows =3D $qb->query(Query{ fields =3D> [ 'id', 'name', 'cost' ], table =3D> 'products', where =3D> 'id=3D' . $productId, }); BTW, if I could have everything I want, I would really like following to = work too: $rows =3D $qb->query({ fields =3D> [ 'id', 'name', 'cost' ], table =3D> 'products', where =3D> 'id=3D' . $productId, }); Note above I omitted Query{} and just used {} when calling $qb->query() = because PHP should be able to see the signature for query() and pass the = initialized values to instantiate a Query object instead of a stdClass = instance. -Mike --Apple-Mail=_904DEA3E-1881-4879-B2BD-E202E35057B6--