Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:119742 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 21255 invoked from network); 17 Mar 2023 15:13:48 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 17 Mar 2023 15:13:48 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 2CE52180511 for ; Fri, 17 Mar 2023 08:13:47 -0700 (PDT) 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.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_PASS,SPF_NONE, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS19151 66.111.4.0/24 X-Spam-Virus: No X-Envelope-From: Received: from out4-smtp.messagingengine.com (out4-smtp.messagingengine.com [66.111.4.28]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Fri, 17 Mar 2023 08:13:46 -0700 (PDT) Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id 685AE5C0139 for ; Fri, 17 Mar 2023 11:13:46 -0400 (EDT) Received: from imap50 ([10.202.2.100]) by compute4.internal (MEProxy); Fri, 17 Mar 2023 11:13:46 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= garfieldtech.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to; s=fm2; t=1679066026; x=1679152426; bh=U/KVVfRj7D crkkYNvsxJYp/j3CVnEJeyHtMyxukQ/y4=; b=ECHamOX06KUXisYI+u/wd6h0Ol rm/ti6R+G30rdLBNOseimiLz3blg84qs7dWMXivvdTW6u+r2bfdZigVEePFPXzZm OrpBgxWYfzagZIvXvmQD45xEfW3wlF6OeTQXofTozvdBLaJ0LdElOB7zDGyjkj7T yd6N65bAzveVjURLKFLp4pWLASTwHwJyON7I4mld0rJOjhTXsBPFoTo5C9rhLPLx 2vxf8D1jAXVDtHWgMzbSojBax/ge8ECAQycuXwY3fmCYHc618Tzy6neGCqylRa5o o+D7CyC802o3noOjJcr6ApZuc3QO5S8cWhA5uHm2UKACsO4LAgHUaeYGyKYQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:sender:subject:subject:to:to:x-me-proxy:x-me-proxy :x-me-sender:x-me-sender:x-sasl-enc; s=fm2; t=1679066026; x= 1679152426; bh=U/KVVfRj7DcrkkYNvsxJYp/j3CVnEJeyHtMyxukQ/y4=; b=R Mr9pY7n9eDdRJgpkoV4yAMfTgL6c0rmxx7NYcGhV+Oa47ZlX/2HgXR2hbP6o8sXn 015oNnlevg5nnKLXHR7+R+lo5IifS4RjujdO08AdDUDSs0zeP3LCkxSa8saYmByI nMHbL7KkJ603+dW8nRTaMHgbqYXwVzsuxxVoYyJKOaxEVgAGrF+6gnAY56lxRNvx 8sra1b65FyDCskQsL4sjBqYaaxHa2z0B2Jtz1NpoifOJE5x7JiaDhYZZOIJ1zoEl CanLcqW7BQR5R2UjvPFS6rBo6bvjhpaGLajHeieejLf9SyJ/W8ilrpA93uzITJIR aDSkkXRmh6hBvRt9fmhPw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvhedrvdefvddgjeefucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne cujfgurhepofgfggfkjghffffhvffutgfgsehtqhertderreejnecuhfhrohhmpedfnfgr rhhrhicuifgrrhhfihgvlhgufdcuoehlrghrrhihsehgrghrfhhivghlughtvggthhdrtg homheqnecuggftrfgrthhtvghrnheplefgfeehtdfgveevheefhfeiteelfeekheeuveef heehhfduiedtkeefgeeuvefgnecuffhomhgrihhnpehorhgrtghlvgdrtghomhenucevlh hushhtvghrufhiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehlrghrrhihsehg rghrfhhivghlughtvggthhdrtghomh X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.nyi.internal (Postfix, from userid 501) id 08BDA1700089; Fri, 17 Mar 2023 11:13:46 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface User-Agent: Cyrus-JMAP/3.9.0-alpha0-221-gec32977366-fm-20230306.001-gec329773 Mime-Version: 1.0 Message-ID: <3cde4fd3-a9bb-400c-933e-150eeb3dc955@app.fastmail.com> In-Reply-To: References: <9975B833-EE24-4ED7-B28E-841B92988BA0@cschneid.com> <1A2CE63B-ECCA-403D-83AC-B1E26279323C@gmail.com> <9a2140b4-97bb-4a9c-90c5-809274c83f75@app.fastmail.com> <88c4a63c-859b-94d5-e314-3399fb2c3fb0@gmail.com> <1a4d4434-7318-4831-9fc0-8b48a6400a62@app.fastmail.com> <73033e11-a521-40a3-a29e-14d575332aea@app.fastmail.com> Date: Fri, 17 Mar 2023 10:13:24 -0500 To: "php internals" Content-Type: text/plain;charset=utf-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] First-class callable partial application From: larry@garfieldtech.com ("Larry Garfield") On Fri, Mar 17, 2023, at 9:45 AM, Larry Garfield wrote: > On Fri, Mar 17, 2023, at 3:54 AM, Micha=C5=82 Marcin Brzuchalski wrote: > >>> As a thought experiment, if we had that syntax and functions that we= re >>> designed to be used with them, it would look like so: >>> >>> function amap(callable $c, iterable $it) { ... } >>> function implode(string $sep, iterable $it) { ... } >>> function length(string $s) { ... } >>> >>> $arr =3D [1, 2, 3]; >>> >>> $a2 =3D amap(...)->partial(chr(...))($arr); >>> >>> $str =3D implode(...)->partial(',')($a2); >>> >>> Or, if combined with pipes: >>> >>> $size =3D $arr >>> |> amap(...)->partial(chr(...)) >>> |> implode(...)->partial(',') >>> |> length(...); >>> >>> Which... is not terrible, especially as it doesn't preclude using hi= gher >>> order functions for more control. >>> >> >> Maybe we could introduce two additional methods on a Closure similar = to >> what Java have >> https://docs.oracle.com/javase/8/docs/api/java/util/function/Function= .html >> * andThen() - which functionality is like a pipe operator >> * apply() - which you can call without the option to bind/rebind and = just >> pass arguments for execution >> >> The pipe operator can be introduced later, but we could already have = the >> functionality on Closure. >> >> The above example might look readable as well: >> >> $size =3D amap(...)->partial(chr(...)) >> ->andThen(implode(...)->partial(',')) >> ->andThen(length(...)) >> ->apply($arr); > > See that brings up a subtle difference between two operations: pipe an= d compose. > > Compose takes two functions A and B and returns a new function that is=20 > logically identical to B(A()). (Or sometimes A(B()), depending on the=20 > language, which is all kinds of confusing.) > > Pipe takes an arbitrary value and unary function and calls the functio= n=20 > with that value immediately, returning the result. > > You can build a pipe equivalent out of compose, although it's a bit=20 > clunky and the semantics are not *quite* identical. (The order of=20 > execution is different, which may or may not matter depending on the=20 > specifics of each call.) > > In the example above, andThen() is acting as a compose operator, while=20 > apply() is just boring function application. It works but it's a bit=20 > clunky compared to a proper pipe. That said, there are user space=20 > libraries that do that. > > Thinking aloud... I said before it would be better to have a native=20 > operator for all of these. (compose, pipe, and partial.) If we=20 > restrict ourselves to Closure objects rather than all callables (since=20 > callables are a lot messier), that does open up some new options. =20 > Specifically, the following are already syntax errors today: > > $c =3D strlen(...); > $d =3D array_map(...); > > $c + $d; // not allowed. > $c . $d; // not allowed, thinks it's string concat and $c isn't string= able. > $c[2, 3]; // not allowed. > $c{5}; // not allowed, thinks it's a string offset. > > So that suggests to me the following: > > * Define $a + $b on closure objects to be a compose operator that=20 > returns a new function equivalent to $b($a( )). It would only work on=20 > unary functions, validate compatible types between the param and retur= n=20 > values, and have the correct type information. > > * Define $c{ ... } as a "partial application" call. The {} body would=20 > be similar to a function call now; or maybe the original PFA syntax=20 > with ? and ... ? Debatable, but the {} would be a better signal to th= e=20 > engine that we're not calling a function, just partially calling. It'= s=20 > also reasonably self-evident to developers. > > Those two together would allow for this: > > (amap{chr(...), ?} + implode{?, separator: ','} + length(...))($arr); > > Which is... pretty nice, really. It's very close to the original PFA=20 > syntax and capabilities, compact, not easily confused with anything=20 > else, type safe, and the engine can handle optimizing around not havin= g=20 > unnecessary interstitial functions internally. > > I still think we should also add a pipe operator |> as well, but that=20 > would be just as compatible with the {}-based PFA syntax. It's also=20 > fairly trivial to implement. > > $size =3D $arr > |> amap{chr(...), ?) > |> implode(',', ?) > |> length(...); > > Thoughts? Wait, my examples were wrong. I forgot that we're talking about only Cl= osures. They should be: > (amap(...){chr(...), ?} + implode(...){?, separator: ','} + length(...= ))($arr); > $size =3D $arr > |> amap(...){chr(...), ?) > |> implode(...)(',', ?) > |> length(...); Not quite as nice, but still not terrible. --Larry Garfield