Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:126961 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id E9C821A00BC for ; Thu, 27 Mar 2025 14:30:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1743085692; bh=Cry6JmcHrcTCFYqnUPcMtZW02PpafxnV8vhNAi1ExOs=; h=References:In-Reply-To:From:Date:Subject:To:From; b=NetBBXXBJUY1viMN4ppY5/i9kcJRezviphxdRq1FNgrZjbVoaN7BH5UGozswb+Qut kjNFAKFDanJ0DH6FcdGv5XiF5BeqYE5JQ31qsfptMqGKEXDivWmbglugzo11xtIv82 EkFCfZse0h/Nhf32K2/6DfHz65DfH1Me+M1/CNHak3qeLgn6Th8cXU5BrnkbObdvsX glbL2IKaAwjFwmZAwUcdl6GOfij+p15IPoWVC6iGdcWRyju8AiYLhrV6yufHPlgHF3 ygPscCoqyL8aiMsLQ/qI+Dvx+l9TCPJRvyiUXv8ZvVDF9PeF+vJe9fJF8cMxfniOOs J4y1ANobYmcTg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 415AA18005B for ; Thu, 27 Mar 2025 14:28:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-3.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-qv1-f45.google.com (mail-qv1-f45.google.com [209.85.219.45]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 27 Mar 2025 14:28:11 +0000 (UTC) Received: by mail-qv1-f45.google.com with SMTP id 6a1803df08f44-6e8f4c50a8fso8774856d6.1 for ; Thu, 27 Mar 2025 07:30:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1743085838; x=1743690638; darn=lists.php.net; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=HtEkMRvjmozRymriRsE/mNL2toWke/GjoEUqPHf8naA=; b=dCegU6jY4Yeh7u7udrwAXUT/zJqizUsKmKAY9CzL1cziw2ryT785VqvlHomMHmyQYA bMw9AujydRJ/mQwE1BcWwNQyK4j6q2ArCzvDIVXL9dKAiS7YsypRrjcGycwfKEHY0lSl s5riNyHHNj+GCtecGW5T0DH5XgAa9st7WwQR8XNfI2gx6Npi9JLLNo8ETyKJRcGjUZV0 Fzz7q4BJgW1XZMg/+YW6n2wLipSelA2QgOs3tTocHB35P/E9+wA1pa2zYESuOuVqxo6+ Z/D0NAEIesdStF5abTW0wsKBn7CflfVdbA1pX20ZF9YuuH20EsQQ3T+6eJLvqgebp9dl excg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1743085838; x=1743690638; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=HtEkMRvjmozRymriRsE/mNL2toWke/GjoEUqPHf8naA=; b=g+IFxpAvDSTzG6FbP3GSeZNuB0AJQTRk/tWkVBBl8vsvfLBBoqLvMgErIawJyHZq78 Bgdyyo0lsuSatwbX4eULzaWEY1qWXFU+dqxgpWlmbUNG97ItaaB1AfBxIOM9H4sm23g4 e30YSOn5cE/HhWxufn07yPKTAMJ7qN3oTIxd3QMXjOPiaWiyPcWTv50N4GeLZceuEH02 bBLMTwhPZej0oNiS1imUQ4F7bqatfYX0urbQtjcI7ralFq2KAmz6Bri59VToLt61DBZU w/73PfgiuTKxZMTONdcU/abogL3MrG9TKJIqBg12Wi5W6AkO6X8Fuvm7uK5PUKl1jSDw 4vIA== X-Gm-Message-State: AOJu0Yxrp0WdUITBp2mPWzZBkRwlW44djUYz1+tZn5ALmssS2L8e+toj YImo9uEvWY8Hy5bEmSSIP+UD5svXOJFOjXz9F79MlGZOtStOumBkyBUNXwf1ZjaO51jzmvpFRzC 5dc4W/cRQB+9JCIxkdzm9aGBNOh63lLHrri4= X-Gm-Gg: ASbGnctX0hsvTXxOpVu1DSfz9AY+VbME2xDEdCVV0DDJuq+WGcGUl9RRqoj4v9rXYa2 C93q6BEKnMklc9SK2gwpkSxbGrARjhSdJ5ynE4tySzjbzVqLxfle0DSNYrOaOGhBniOJ7FkJObx v+ThVBkPvcj92Hz6r19AhiMgu2JZow+fKi/mRNVo3A62xuhxr+kPS7oB107/W8 X-Google-Smtp-Source: AGHT+IFUxTVG7ig+btfV6MJFC9KZbVcAXNujG2DBu1iIk+inuePUr+qvBKOU1Iggw07bQoygBc3dm80lYzhxeUjE98A= X-Received: by 2002:a05:6214:19eb:b0:6ea:d393:962a with SMTP id 6a1803df08f44-6ed238429b0mr43066006d6.1.1743085838336; Thu, 27 Mar 2025 07:30:38 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Thu, 27 Mar 2025 15:30:26 +0100 X-Gm-Features: AQ5f1Jqrf7K92EEsYgAU76-y6LxU-MLRPpwOcCfRvXQ8fuddL7nD8yesGW-NBWQ Message-ID: Subject: Re: [PHP-DEV] [RFC] Pipe Operator (again) To: php internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable From: tovilo.ilija@gmail.com (Ilija Tovilo) Hi Larry Sorry for the late response. On Fri, Feb 7, 2025 at 5:58=E2=80=AFAM Larry Garfield wrote: > > https://wiki.php.net/rfc/pipe-operator-v3 We have already discussed this topic extensively off-list, so let me bring the list up-to-date. The current pipes proposal is elegantly simple. This has many upsides, but it comes with an obvious limitation: It only works well when the called function takes only a single argument. $sourceCode |> lexer(...) |> parser(...) |> compiler(...) |> vm(...) Such code is nice, but is also quite niche. I have argued off-list that the predominant use-case for pipes are arrays and iterators (including strings immediately split into chunks), and it seems most agree. However, most array/iterator functions (e.g. filter, map, reduce, first, all, etc.) don't fall into the one-parameter category. A slightly simplified example from the RFC: $result =3D "Hello World" |> str_split(...) |> fn($x) =3D> array_map(strtoupper(...), $x) |> fn($x) =3D> array_filter($x, fn($v) =3D> $v !=3D 'O'); IMO, this is harder to understand than the alternative of using multiple statements with a temporary variable. $tmp =3D "Hello World"; $tmp =3D str_split($tmp); $tmp =3D array_map(strtoupper(...), $tmp); $result =3D array_filter($tmp, fn($v) =3D> $v !=3D 'O'); The RFC has a solution for this: Partial function application [1]. $result =3D "Hello World" |> str_split(...) |> array_map(strtoupper(...), ?) |> array_filter(?, fn($v) =3D> $v !=3D 'O'); This still causes more cognitive overhead than it should, at least to me. * The placement of ? is hard to detect, especially when it's not the first argument. * The user now has to think about immediately-invoked closures that exist solely for argument-reordering. The closure can be elided through the optimizer, but we cannot elide the additional cognitive overhead in the user. * The implementation of ? is significantly more complex than that of pipes, making the supposed simplicity of pipes somewhat misleading. If my assumption is correct that the primary use-case for pipes are arrays, it might be worth investigating the possibility of introducing a new iterator API, which has been proposed before [2], optimized for pipes. Specifically, this API would ensure consistent placement of the subject, i.e. the iterable in this case, as the first argument. Pipes would no longer have the form of expr |> expr, where the right-hand-side is expected to return a callable. Instead, it would have the form of expr |> function_call, where the left-hand-side is implicitly inserted as the first parameter of the call. namespace Iter { function map(iterable $iterable, \Closure $callback): \Iterator; function filter(iterable $iterable, \Closure $callback): \Iterator; } namespace { use function Iter\{map, filter}; $result =3D "Hello World" |> str_split() |> map(strtoupper(...)) |> filter(fn($v) =3D> $v !=3D 'O'); } This is the same approach taken by Elixir [3]. It has a few benefits: * We don't need to think about closures that are immediately invoked, because there are none. The code is exactly the same as if you had written it through nested function calls. This simplifies things significantly for both the engine and the user. * It closely resembles code that would be written in an object-oriented manner, making it more familiar. * It is the shortest and most readable of all the proposed options. As with everything, there are downsides. * It only works well for subject-first APIs. There are not an insignificant number of existing functions that do not follow this convention (e.g. explode(), preg_match(), etc.). That said, explode(' ', $s) |> filter($c1) |> map($c2) still composes well, given explode() is usually first first in the chain, while preg_match() is rarely chained at all. * People have voiced concerns for potential confusion regarding the right-hand-side. It may not be any arbitrary expression, but is restricted to a function call. Hence, `$param |> $myClosure` is not valid code, requiring additional braces: `$param |> $myClosure()`. This approach resembles the -> operator, where at least conceptually, the left-hand-side is implicitly passed as a $this parameter. However, the spaces between |> do not signal this fact as well, making it look like the right-hand-side is evaluated separately. Potentially, a different symbol might work better. Internal reactions to this idea were mixed, so I'm interested to hear what the community thinks about it. Ilija [1] https://wiki.php.net/rfc/partial_function_application [2] https://externals.io/message/118896 [3] https://elixirschool.com/en/lessons/basics/pipe_operator