Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:107094 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 71401 invoked from network); 14 Sep 2019 23:11:29 -0000 Received: from unknown (HELO php-smtp3.php.net) (208.43.231.12) by pb1.pair.com with SMTP; 14 Sep 2019 23:11:29 -0000 Received: from php-smtp3.php.net (localhost [127.0.0.1]) by php-smtp3.php.net (Postfix) with ESMTP id 11D192C2E08 for ; Sat, 14 Sep 2019 13:47:56 -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.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: X-Spam-Virus: No Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) (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:47:55 -0700 (PDT) Received: by mail-wm1-x332.google.com with SMTP id r17so4336021wme.0 for ; Sat, 14 Sep 2019 13:47:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:to:references:from:message-id:date:user-agent:mime-version :in-reply-to:content-transfer-encoding:content-language; bh=lx+wM+7FEwH6NX87b7zemo4Gx1DMHhbAl6Q6Z3UkWfI=; b=D49Qu7lulCR0R6TA8eKkdqxy8eEM/+6gNhiNF3sJxnUkt/VVGozL47Jp4qzonIGdRh 2dRT1ibn80g2zvjaQrasZaEqH0tKgSJY+KowXcdUU+gihNfsQRmzZhWo+Z0Il5jDcnR6 WcxD5dlNf0swgh/LWL5rBITBZ6mEctkQAuq5J3uRkjYucZJaOwkq8p8e7hrmE2o7vGE9 bvJvm9u+0s9blJoIV5uMnadyN7d+IMIOEoQ5ZyoxrLEZ6MfOlpSDVjVtZM6cfqiN2wM4 oXcGC+1mSjG379O9cBaIGduUbf8rvNqO2IovtSwRW7HRv7ziWf0RcYQ973dWiOmJr51G JCMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-transfer-encoding :content-language; bh=lx+wM+7FEwH6NX87b7zemo4Gx1DMHhbAl6Q6Z3UkWfI=; b=iFk1ahAZAFmfny09lkyegSmPXQlED92ht2h5+lrUXJ9PRY8/dhYq2wgfagsBZl+15u 9HazJaa89xXAMrNeF96jDEFvFzYkRg3OBFDci/cxKmYfIc6+m7gkd2DiSULEyDINGzR1 BJL5xCci+9wJxoiDZfB9ILknv1VimJuBa02BDLUqH1qCMvJpUlS9EBiCC2U6IwocrQV3 sUWH2au8CnNCFsBSuywuPHzLPgNoj6L0S1lEE0jtRFEP5L6bjt9H16kGurosMrkV4cPf gi/5EtBRdOCfuaf9kC1f7v8zF8qxpzXjNTj/DuN/HhceP57oaNXNYMaJ5RIX4YVUldCN R1kw== X-Gm-Message-State: APjAAAUn/QBhtU1YQmSVgpHaE2srdlxP832smYIOktCzD6HvjnuKFYKK yxBGLZu6mFsYvxlr4Wq0vp08SOur X-Google-Smtp-Source: APXvYqxmwFrowc19iSA0hvBRAxJ3Hg0kLWiWaB7lvfU7OPeeI7fp0DvNhwGsa5EIJaGQvc8ob+NtTA== X-Received: by 2002:a1c:7a05:: with SMTP id v5mr7850546wmc.173.1568494073610; Sat, 14 Sep 2019 13:47:53 -0700 (PDT) Received: from [192.168.0.16] (cpc84253-brig22-2-0-cust114.3-3.cable.virginm.net. [81.108.141.115]) by smtp.googlemail.com with ESMTPSA id e6sm6206482wrp.91.2019.09.14.13.47.52 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 14 Sep 2019 13:47:52 -0700 (PDT) To: internals@lists.php.net References: <0275793C-EC66-4693-B74F-B794E6FF0AC6@newclarity.net> Message-ID: <4c36506a-c110-4b1f-a902-482ba6b8c55e@gmail.com> Date: Sat, 14 Sep 2019 21:47:51 +0100 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:60.0) Gecko/20100101 Thunderbird/60.9.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Content-Language: en-GB X-Envelope-From: Subject: Re: [PHP-DEV] [RFC] Object Initializer From: rowan.collins@gmail.com (Rowan Tommins) On 14/09/2019 09:49, Mike Schinkel wrote: > 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. I think that's only true because you've actually proposed a number of related but different features. > 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 = new Car({ > yearOfProduction => 1975, > vin => "12345678", > engine => { > type => "v8", > capacity => "270ci", > }, > }) I'm not sure what "nested parameters" could ever mean, other than initializing additional objects. And if these are additional objects, then named parameters work just as well in the nested case as in the non-nested one: $car = new Car( yearOfProduction => 1975, vin => "12345678", engine => new Engine( type => "v8", capacity => "270ci", ), ); Indeed, since these are named parameters to a function, you could also use a static factory method in the same place, which special-case initializer syntax wouldn't allow you to: $car = new Car( yearOfProduction => 1975, vin => "12345678", engine => Engine::fromSerialNo( format => 'ISO', id => '123abc567' ), ); > 2. Local usage > ... > $stats = new class { > int total => 0, > int mean => 0, > int average => 0, > int median => 0, > } This already works: $stats = new class { var int $total = 0; var int $mean = 0; var int $average = 0; var int $median = 0; }; What doesn't currently work is using variables from lexical scope in the definition: $stats = new class { var int $total = $a + $b; var int $mean = $c; var int $average = $d; var int $median = $e; }; However, initializer syntax on its own doesn't solve this, because you wouldn't be able to specify the types for the properties; just combining existing syntax with initializers would give something like this: $stats = new class { var int $total; var int $mean; var int $average; var int $median; }{ total => $a + $a, mean => $c, average => $d, median => $e } You could certainly make the syntax of initializers and anonymous class definitions similar, but they're not really the same thing. > 3. Passing to subroutines. This is a contrived example, but it is emblematic of code I write daily. > > class Query { > public string[] $fields; > public string $table; > public Join $join; > public string[] $where; > } > class QueryBuilder { > function query(Query $query):object[] { > if ( ! $this->validate($query) ) { > throw new Exception("Dude!"); > } > $query = $this->build($query); > return $this->run($query); > } > function validate(Query $query) { > ... > } > function build(Query $query) { > ... > } > ... > } > $qb = new QueryBuilder(); > $rows = $qb->query(Query{ > fields => [ 'id', 'name', 'cost' ], > table => 'products', > where => 'id=' . $productId, > }); The QueryBuilder class in this example has not benefited from object initializers at all; passing the Query object rather than separate parameters is a refactoring you can (and probably should) do right now. The Query class, meanwhile, is no different from the previous examples, and the call would look just the same with named parameters to the constructor: $qb = new QueryBuilder(); $rows = $qb->query(new Query( fields => [ 'id', 'name', 'cost' ], table => 'products', where => 'id=' . $productId, )); As before, the definition of the class itself is simpler, but I think a short-hand syntax for constructors would be a better compromise than a syntax that only worked with public properties and parameterless constructors. > BTW, if I could have everything I want, I would really like following to work too: > > $rows = $qb->query({ > fields => [ 'id', 'name', 'cost' ], > table => 'products', > where => 'id=' . $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. This is an interesting additional proposal, which I admit would be awkward to implement without initializer syntax. One limitation is that it only works if dependencies are declared as concrete classes not interfaces - which like public properties is not "wrong" per se, but limits the scope of the feature. > Conceptually let me ask, How is { yearOfProduction = 1975, vin = "12345678"} really any different from an instance of an anonymous class with the properties yearOfProduction and vin? Named parameters are (or would be) a way of setting a bunch of variables; they're not linked as an object of any sort, so I don't think there's a natural comparison to anonymous classes at all. > we could use something like func_get_args(ARGS_OBJECT) to allow us to capture the grouped parameters as an instance of an object of anonymous class containing properties for each named parameter This again is an interesting proposal, but completely unrelated to object initializer syntax. If I understand it right, the next example relies on the combination of anonymous class initialisers, named class initialisers, and named parameters all using the same syntax, with context determining whether someFunction({bar => 1, baz => 2}) means a) pass two integer parameters; b) create and pass a single anonymous class instance; or c) create and pass a single instance of some class. The examples look really neat on their own, but imagine coming on that syntax in someone else's code and trying to work out what it was doing. There's definitely some interesting ideas here, but they're not all part of one feature, and they all rely on particular ways of structuring your code. Regards, -- Rowan Tommins (né Collins) [IMSoP]