Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:112675 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 86247 invoked from network); 30 Dec 2020 19:08:13 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 30 Dec 2020 19:08:13 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 86A691804F3 for ; Wed, 30 Dec 2020 10:42:59 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.2 X-Spam-Virus: No X-Envelope-From: Received: from mail-lf1-f53.google.com (mail-lf1-f53.google.com [209.85.167.53]) (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, 30 Dec 2020 10:42:59 -0800 (PST) Received: by mail-lf1-f53.google.com with SMTP id m12so39516454lfo.7 for ; Wed, 30 Dec 2020 10:42:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc:content-transfer-encoding; bh=JQXuukpHTqsJjntKKMvvu5xpTWcq10lYKXX0scwu8pQ=; b=eY9t1AuEmBdEwp3Kru33zJl082ZSSRTJKDFKvZ+zAQPta5scW0R2139SrEh4yiL03A tPUdOmXreiGKU0DiNGRI2fHlhUs1McCjRBLYhH2smkw3he8vhurEjcxUR958izRJZAGn LrEc7pt9pNkoy+tDDZMLHIGHUPWmstBINLNYhdxULek4vk2aEWosCxTdLlWe6ZJhPCcK y7hchFd9LKH9kXUGeVC21H2X13JgnF9uvnNUv/goKwTLICOw7asf3oe7a2+7qcMK7SY8 Ex2nZtJMwL0tFO09bA55kzGN6SI0b9C7mkX1PfL9aoW6qLyKPWZMzMbA8K4s5RaXKr6e 6x9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc:content-transfer-encoding; bh=JQXuukpHTqsJjntKKMvvu5xpTWcq10lYKXX0scwu8pQ=; b=dKO+IZDheKsh07FrB3n+YdqcY4Y1jJoD2lMSwYIlJanLw6J+Gy3WGG3kpmWzjweqnH R+U9CA/kbOV4KzntcX4CNNbRBC/mW/V95Y56IvelDV7J2jZwOngfXX5QiUSPviw8AAAt v+kZkifNyZ7VRLw3luv5kgvtXkKye/FxkQXlFNLqJg3ShfblTyDMcSkwcMKSon+UDMrD O2+3XkCvIuRPE1GLxNt13JRJR+0uqKrfrovbZECKHtU0B/Y76DBEWJT94n1ALhmeOBA1 3lmOQckletd0l95NCnVTfJn60R1sn+RDgoEHU9OAgehBefYWx5JxNIpQfAHORm3F43r0 p7Mw== X-Gm-Message-State: AOAM5310QABLYIMTYV44DhA7ma7McljjkoizDNF6gL2hgqpKJsxj7R1T UAsWue4NCjWODN/jNI0s4nvgC44qMwibFypNpaw= X-Google-Smtp-Source: ABdhPJyg0Ll7U8mkYRwn+2NrbnRhjlR7SBBeW7PoSH9sdTqjwrHniXEUU68p9P+g4/4anTT8VYuP7nS0CpGH9+c0Zcc= X-Received: by 2002:a05:6512:208c:: with SMTP id t12mr24561969lfr.165.1609353777222; Wed, 30 Dec 2020 10:42:57 -0800 (PST) MIME-Version: 1.0 Received: by 2002:ab3:7110:0:0:0:0:0 with HTTP; Wed, 30 Dec 2020 10:42:56 -0800 (PST) In-Reply-To: <7f4fe9ca-1c20-6f69-cef0-a9718af742a3@gmail.com> References: <1d0abb04-4987-43a9-85bc-bccc3bd6be9a@www.fastmail.com> <03108284-740a-4a5d-130f-15b2e67e9df9@mabe.berlin> <459d7ff7-e553-dce9-7d43-c3b1e772e572@gmail.com> <7f4fe9ca-1c20-6f69-cef0-a9718af742a3@gmail.com> Date: Wed, 30 Dec 2020 18:42:56 +0000 Message-ID: To: Rowan Tommins Cc: internals@lists.php.net Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] Analysis of property visibility, immutability, and cloning proposals From: olleharstedt@gmail.com (=?UTF-8?Q?Olle_H=C3=A4rstedt?=) 2020-12-30 18:15 GMT, Rowan Tommins : > On 30/12/2020 13:49, Olle H=C3=A4rstedt wrote: >> Uniqueness is when you only allow _one_ reference to an object (or >> bucket of memory). >> [...] >> >> You can compare a builder pattern with immutability vs non-aliasing >> (uniqueness): >> >> ``` >> // Immutable >> $b =3D new Builder(); >> $b =3D $b->withFoo()->withBar()->withBaz(); >> myfun($b); // $b is immutable, so $b cannot be modified by myfun() >> return $b; >> ``` >> >> ``` >> // Uniqueness >> $b =3D new Builder(); // class Builder is annotated as non-aliasing/uni= que >> $b->addFoo(); >> $b->addBar(); >> $b->addBaz(); >> myfun(clone $b); // HAVE TO CLONE TO NOT THROW EXCEPTION. >> return $b; >> ``` > > > Thanks, I can see how that solves a lot of the same problems, in a very > robustly analysable way. > > However, from a high-level user-friendliness point of view, I think > "withX" methods are actually more natural than explicitly cloning > mutable objects. > > Consider the case of defining a range: firstly, with plain integers and > familiar operators: > > $start =3D 1; > $end =3D $start + 5; > > This models integers as immutable values, and + as an operator which > returns a new instance. If integers were mutable but not aliasable, we > would instead write something like this: > > $start =3D 1; > $end =3D clone $start; > $end +=3D 5; // where +=3D would be an in-place modification, not a > short-hand for assignment > > I think the first more naturally expresses the desired algorithm. It's > therefore natural to want the same for a range of dates: > > $start =3D MyDate::today(); > $end =3D $start->withAddedDays(5); > > vs > > $start =3D MyDate::today(); > $end =3D clone $start; > $end->addDays(5); Sure, this is a good use-case for immutability. :) > > > To put it a different way, value types naturally form *expressions*, > which mutable objects model clumsily. It would be very tedious if we had > to avoid accidentally mutating the speed of light: > > $e =3D (clone $m) * ((clone $c) ** 2); Using a variable on right-hand side does not automatically create an alias, so in the above case you don't have to use clone. A more motivating example for uniqueness is perhaps a query builder. ``` $query =3D (new Query()) ->select(1) ->from('foo') ->where(...) ->orderBy(..) ->limit(); doSomething($query); doSomethingElse($query); ``` In the above snippet, we don't know if doSomething() will change $query and cause a bug. The issue can be solved with an immutable builder, using withSelect(), withWhere(), etc, OR it's solved with uniqueness, forcing a clone to avoid creating a new alias (passing $query to a function creates an alias inside that function). The optimisation is the same as in my previous example, avoiding copying $query multiple times during build-up. > > >> The guarantee in both above snippets is that myfun() DOES NOT modify >> $b before returning it. BUT with immutability, you have to copy $b >> three times, with uniqueness only one. > > > I wonder if that difference can be optimised out by the > compiler/OpCache: detect clones that immediately replace their original, > and optimise it to an in-place modification. In other words, compile > $foo =3D clone $foo with { x: 42 } to $foo->x =3D 42, even if the clone i= s > actually in a "withX" method. I guess OCaml/Haskell does stuff like this, since everything is immutable by default there. Let's ask them? Unless someone here already knows? :) Olle