12 ms·
PHP 8.5 adds pipe operator
- bapak 1y agoMeanwhile the JS world has been waiting for 10 years for this proposal, which is still in stage 2 https://github.com/tc39/proposal-pipeline-operator/issues/232#issuecomment-2784879225 https://github.com/tc39/proposal-pipeline-operator/issues/23...
- wouldbecouldbe 1y agoIt’s really not needed, syntax sugar. With dots you do almost the same. Php doesn’t have chaining. Adding more and more complexity doesn’t make a language better.
- EGreg 1y agoIt’s not really chaining More like thenables / promises
- wouldbecouldbe 1y agoIt looks like chaining, but with possibility of adding custom functions?
- bapak 1y agoIt's chaining without having to vary the return of each function. In JS you cannot call 3.myMethod(), but you could with 3 |> myMethod
- cyco130 1y agoIt requires parentheses `(3).myMethod()` but you can by monkey patching the Number prototype. Very bad idea, but you absolutely can.
- senfiaj 1y agoYou can just add extra dot: `3..myMethod()`.
- EGreg 1y agoNot only that In chaining, methods all have to be part of the same class. In C++ we had this stuff ages ago, it’s called abusing streaming operators LMAO
- bapak 1y agoNothing is really needed, C89 was good enough. Dots are not the same, nobody wants to use chaining like underscore/lodash allowed because it makes dead code elimination impossible.
- troupo 1y ago> With dots you do almost the same. Keyword: almost. Pipes don't require you to have many different methods on every possible type: https://news.ycombinator.com/item?id=44794656 https://news.ycombinator.com/item?id=44794656
- te_chris 1y agoDots call functions on objects, pipe passes arguments to functions. Totally missing the point.
- Martinussen 1y agoWhen you say chaining, do you mean autoboxing primitives? PHP can definitely do things like `foo()->bar()?->baz()`, but you'd have to wrap an array/string yourself instead of the methods being pulled from a `prototype` to use it there.
- chilmers 1y agoI'm tired of hearing the exact same arguments, "not needed", "just syntax sugar", "too much complexity", about every new syntax feature that gets added to JS. Somehow, once they are in the language, nobody's head explodes, and people are soon using them and they become uncontroversial. If people really this new syntax will make it harder to code in JS, show some evidence. Produce a study on solving representative tasks in a version of the language with and without this feature, showing that it has negative effects on code quality and comprehension.
- robertlagrant 1y agoPresumably it's up to the change proposers to produce said study showing the opposite.
- 38 1y ago[dead]
- purerandomness 1y agoIf your team prefers not to use this new optional feature, just enable a PHPStan rule in your CI/CD pipeline that prevents code like this getting merged.
- hajile 1y agoChaining requires creating a class and ensuring everything sticks to the class and returns it properly so the chain doesn't blow up. As you add more options and do more stuff, this becomes increasingly hard to write and maintain. If I'm using a chained library and need another method, I have to understand the underlying data model (a leaky abstraction) and also must have some hack-ish way of extending the model. As I'm not the maintainer, I'm probably going to cause subtle breakages along the way. Pipe operators have none of these issues. They are obvious. They don't need to track state past the previous operator (which also makes debugging easier). If they need to be extended, look at your response value and add the appropriate function. Composition (whether with the pipe operator or not) is vastly superior to chaining.
- lacasito25 1y agoin typescript we can do this let res res = op1() res = op2(res.op1) res = op3(res.op2) type inference works great, and it is very easy to debug and refactor. In my opinion even more than piping results. Javascript has enough features.
- avaq 1y agoNot only have we been waiting for 10 years, the most likely candidate to go forward is not at all what we wanted when the proposal was created: We wanted a pipe operator that would pair well with unary functions (like those created by partial function application, which could get its own syntax), but that got rejected on the premise that it would lead to a programming style that utilizes too many closures[0], and which could divide the ecosystem[1]. Yet somehow PHP was not limited by these hypotheticals, and simply gave people the feature they wanted, in exactly the form it makes most sense in. [0]: https://github.com/tc39/proposal-pipeline-operator/issues/221#issue-999806278 https://github.com/tc39/proposal-pipeline-operator/issues/22... [1]: https://github.com/tc39/proposal-pipeline-operator/issues/233#issuecomment-928217657 https://github.com/tc39/proposal-pipeline-operator/issues/23...
- xixixao 1y agoI guess partially my fault, but even in the article, you can see how the Hack syntax is much nicer to work with than the functional one. Another angle is “how much rewriting does a change require”, in this case, what if I want to add another argument to the rhs function call. (I obv. don’t consider currying and point-free style a good solution)
- lexicality 1y agoAm I correct in my understanding that you're saying that the developers of the most widely used JS engine saying "hey we can't see a way to implement this without tanking performance" is a silly hypothetical that should be ignored?
- avaq 1y agoThey can't implement function application without tanking performance? I find that hard to believe. Especially considering that function application is already a commonly used (and, dare I say: essential) feature in the language, eg: `Math.sqrt(2)`. All we're asking for is the ability to rewrite that as `2 |> Math.sqrt`. What they're afraid of, my understanding goes, is that people hypothetically, may start leaning more on closures, which themselves perform worse than classes. However I'm of the opinion that the engine implementors shouldn't really concern themselves to that extent with how people write their code. People can always write slow code, and that's their own responsibility. So I don't know about "silly", but I don't agree with it. Unless I misunderstood and somehow doing function application a little different is actually a really hard problem. Who knows.
- fergie 1y agoGood- the [real world examples of pipes in js](https://github.com/tc39/proposal-pipeline-operator?tab=readme-ov-file#real-world-examples https://github.com/tc39/proposal-pipeline-operator?tab=readm...) are deeply underwhelming IMO.
- defraudbah 1y agodo not let me start on monads in golang... both are going somewhere and super popular though
- 77pt77 1y agoBest you'll git will be 3 new build systems and 10 new frameworks
- epolanski 1y agoSadly they went obsessing over pipes with promises which don't fit the natural flow. Go explain them that promises already have a natural way to chain operations through the "then" method, and don't need to fit the pipe operator to do more than needed.
- sandreas 1y agoWhile I appreciate the effort and like the approach in general, in this use case I really would prefer extensions / extension functions (like in Kotlin[1]) or an IEnumerable / iterator approach (like in C#). $arr = [ new Widget(tags: ['a', 'b', 'c']), new Widget(tags: ['c', 'd', 'e']), new Widget(tags: ['x', 'y', 'a']), ]; $result = $arr |> fn($x) => array_column($x, 'tags') // Gets an array of arrays |> fn($x) => array_merge(...$x) // Flatten into one big array |> array_unique(...) // Remove duplicates |> array_values(...) // Reindex the array. ; feels much more complex than writing $result = $arr->column('tags')->flatten()->unique()->values() having array extension methods for column, flatten, unique and values. 1: https://kotlinlang.org/docs/extensions.html#extension-functions https://kotlinlang.org/docs/extensions.html#extension-functi...
- troupo 1y agoThe advantage is that pipes don't care about the type of the return value. Let's say you add a reduce in the middle of that chain. With extension methods that would be the last one you call in the chain. With pipes you'd just pipe the result into the next function
- sandreas 1y agoYeah, I agree. That's an advantage of pipes - although much harder to read and write than chained methods in my opinion. The use-case in the article could still be solved easier with extension methods in my opinion :-)
- troupo 1y agoYeah, the examples should also show that you can use arbitrary functions, not just library functions. E.g. your own business logic, validation etc.
- cess11 1y agoPHP has traits, just invent that API, put it in a trait and add it to your data classes.
- librasteve 1y agoraku has had feed operators like this since its inception # pipeline functional style (1..5) ==> map { $_ * 2 } ==> grep { $_ > 5 } ==> say(); # (6 8 10) # method chain OO style (1..5) .map( * * 2) .grep( * > 5) .say; # (6 8 10) uses ==> and <== for leftward true it is syntax sugar, but often the pipe feed is quite useful to make chaining very obvious https://docs.raku.org/language/operators#infix_==%3E https://docs.raku.org/language/operators#infix_==%3E
- gbalduzzi 1y agoI like it. I really believe the thing PHP needs the most is a rework of string / array functions to make them more consistent and chain able. Now they are at least chainable. I'm not a fan of the ... syntax though, especially when mixed in the same chain with the spread operator
- noduerme 1y agoAgree, the ... syntax feels confusing when each fn($x) in the example uses $x as the name of its argument. My initial instinct would be to write like this: `$result = $arr |> fn($arr) => array_column($arr, 'tags') // Gets an array of arrays |> fn($cols) => array_merge(...$cols)` Which makes me wonder how this handles scope. I'd imagine the interior of some chained function can't reference the input $arr, right? Does it allow pass by reference?
- Einenlum 1y agoYou can write it this way. The parameter name is arbitrary. And no, to my knowledge you can't access the var from the previous scope
- cess11 1y agoYou can do function ($parameter) use ($data) { ... } to capture stuff from the local environment. Edit: And you can pass by reference: > $stuff = [1] = [ 1, ] > $fn = function ($par) use (&$stuff) { $stuff[] = $par; } = Closure($par) {#3980 …2} > $fn(2) = null > $stuff = [ 1, 2, ] Never done it in practice, though, not sure if there are any footguns besides the obvious hazards in remote mutation.
- noduerme 1y agoidk if it counts as an obvious hazard, but being able to modify the original input by reference within a chain of piped functions definitely makes it a lot harder to reason about what that sequence might be doing. Particularly since we assume that arrays are passed by reference anyway unless the function you're calling is making a shallow copy of their elements. My feeling is that this makes the code less legible. I'd rather write 5 lines of code that mutate an object or return a copy than do a pipe this way. I'm sort of not excited to start running into examples of this in the wild.
- keyle 1y agoC'mon Dart! Follow up please. Go is a lost cause...
- tayo42 1y agoI feel like a kindergartener writing go. I wish another language got popular in the space go is used for.
- jillesvangurp 1y agoKotlin is shaping up slowly. It's kind of there with a native compiler that is getting better with each release and decent multiplatform libraries. It's a bit weak with support for native libraries and posix stuff. But that's a fixable issue; it just needs more people working on that. For example ktor (one of the server frameworks) can actually work with Kotlin native but it's not that well supported. This is not using Graal or any of the JVM stuff at runtime (which of course is also a viable path but a lot more heavyweight). With Kotlin native, the Kotlin compiler compiles directly to native code and uses multiplatform libraries with native implementations. There is no Java standard library and none of the jvm libraries are used. The same compiler is also powering IOS native with Compose multiplatform. On IOS libraries are a bit more comprehensive and it's starting to become a proper alternative to things like flutter and react native. It also has pretty decent objectc and swift integration (both ways) that they are currently working on improving. In any case, it's pretty easy to write a command line thingy in Kotlin. Use Klikt or similar for command line argument parsing. Jetbrains seems to be neglecting this a bit for some reason. It's a bit of a blind spot in my view. Their wasm support has similar issues. Works great in browsers (and supported with compose as well) but it's not a really obvious choice for serverless stuff or edge computing just yet; mainly because of the library support. Swift is a bit more obvious but has the issue that Apple seems to think of it as a library for promoting vendor lockin on their OS rather than as a general purpose language. Both have quite a bit of potential to compete with Go for system programming tasks.
- tayo42 1y agothats interesting, ill have to keep an eye on that. kotlin always in my mind was android and jvm so i never paid attention to it
- avkrpatel 1y ago[flagged]
- phplovesong 1y agoThe stdlib is so inconsistent this will be a nightmare. Optionally with a better language you know what order params as passed (array_map / array_filter), but in PHP its an coin toss. This feels very bolted on and not suited for the stdlib at all. PHP devs should instead FIRST focus on full unicode support (no, the mb_real_uppercase wont do), and only then focus on a new namespaced stdlib with better design.
- Einenlum 1y agoThis. We definitely need a better stdlib with appropriate data structures
- allan_s 1y agoit's a chicken and the egg problem I think initiative like this drive a need for a more consistent, and even if slow, PHP has been deprecated/reworking its stdlib so I'm hopeful on this.
- foul 1y ago>The stdlib is so inconsistent this will be a nightmare. I think that callables will end with being useless in this context and everyone will pipe closures to put that $x wherever the stdlib imposes.
- goykasi 1y agoIs array_map and array_filter the common argument? One works against a single element, whereas the other works against multiple. What would you suggest a better param order? Do you know that array_walk exists?
- lordofgibbons 1y agoWhy doesn't PHP remove the horrid $ symbol for variables and the -> symbol for calling methods? I think those alone would do a lot more for its perception and adoption than adding the pipe operator.
- phatskat 1y agoI actually don’t mind them, and I’ve been out of daily PHP work for a few years now. When I see people denote internal variables with _ or elements with $ in JS, it rubs me the wrong way, but in PHP the $ is kind of nice. I also prefer the look of ->, it’s _cool_
- kijin 1y agoOther languages have all sorts of oversized arrows, like ==> and >>>. -> in PHP and C++ looks clean by comparison. I'll never forgive them for the brain fart they made of the namespace separator, though.
- LeonM 1y ago> I'll never forgive them for the brain fart they made of the namespace separator, though. You mean the backslash? What's wrong with that?
- account42 1y agoTo someone not already familiar with PHP it looks like you are trying to escape something.
- kijin 1y agoThe backslash is universally reserved as an escape character. It was decided almost 20 years ago so I'm totally used to it and there's no point arguing about it anymore. But the decision to reuse the backslash as a namespace separator still causes inconvenience from time to time. For example, when you write PSR-4 configuration in composer.json, all the backslashes need to be doubled, including (and especially!) the trailing backslash.
- JaggerJo 1y agoThanks F#!
- ChocolateGod 1y agoWhy not just make types psuedo-objects? $myString.trim().replace("w", "h"); Which has the advantage of also offering a clean alternative to the fragmented stdlib.
- reddalo 1y agoI agree. But in PHP it would probably be like this: $myString->trim()->replace("w", "h");
- troupo 1y agoBecause pipes don't care about the type your function returns. And you don't need hundreds of methods on each type just in case. You just pipe the result of the previous function to the next one. And those functions can be business logic, or validation, or... Not just object methods
- williamdclt 1y ago> Why not just make types psuedo-objects? With this sort of "just" I could build Paris out of matchsticks
- account42 1y agoBecause duplicating the stdlib is probably not a good idea.
- habibur 1y agoI tried to emulate something similar with PHP at one point. But the problem with PHP was parameter order. Especially in functions like array_key_exists() the array element is the 2nd parameter, while pipe operator expects the object to work on be the 1st parameter, the array in these cases. I believe they have solved this problem by now. Though no idea how.
- kijin 1y agoThe usual solution is to wrap it with a closure. function($x) { return array_key_exists('needle', $x); } Or using the arrow function syntax: fn($x) => array_key_exists('needle', $x) The same trick also helps when you need to use functions with mandatory extra parameters, functions with pass-by-value parameters, etc.
- moebrowne 1y agoIf the Partial Function Application RFC passes then the closure wont be necessary https://wiki.php.net/rfc/partial_function_application_v2 https://wiki.php.net/rfc/partial_function_application_v2
- cess11 1y ago"A major limitation of the pipe operator is that all the callables in the chain must accept only one required parameter. For built-in functions, if the function does not accept any parameters, it cannot be used in a chain. For user-land PHP functions, passing a parameter to a function that does not accept any parameters does not cause an error, and it is silently ignored. With the pipe operator, the return value of the previous expression or the callable is always passed as the first parameter to the next callable. It is not possible to change the position of the parameter." https://php.watch/versions/8.5/pipe-operator https://php.watch/versions/8.5/pipe-operator In the light of these limitations I would not call the Elixir implementation "slightly fancier". I'm not so sure I'll be upgrading my local PHP version just for this but it's nice that they are adding it, I'm sure there is a lot of library code that would look much better if rewritten into this style.
- ossusermivami 1y agoi wish python had something liek that to be honest
- kh_hk 1y agoone can dream but i wouldn't keep high hopes. I feel functional patterns are left as second class citizens in python.
- abrookewood 1y agoI love the pipe operator - one of the things I dig about Elixir though many languages have it. It's so much easier to reason about: $result = $arr |> fn($x) => array_column($x, 'tags') |> fn($x) => array_merge(...$x) |> array_unique(...) |> array_values(...) VS array_values(array_unique(array_merge(...array_column($arr, 'tags'))));
- qwertox 1y agoI don't see how this is hard to reason about, assuming this is the resulting code when using variables: $tags = ...array_column($arr, 'tags'); $merged_tags = array_merge($tags); $unique_tags = array_unique($merged_tags); $tag_values = array_values($unique_tags); It also makes it easier to inspect the values after each step.
- r34 1y agoYour version includes 4 variables. Pipes don't create those intermediate variables, so they are more memory efficient. Readability is mostly matter of habit. One reads easily what he/she is used to read.
- girvo 1y ago> so they are more memory efficient They can be. It depends on the language, interpreter, compiler, and whether you do anything with those intermediate variables and the optimiser can get rid of them.
- DataDaemon 1y agoThis will be the year of PHP. People are tired of JS.
- beardyw 1y agoI admire your conviction.
- Timwi 1y agoI am indeed tired of JS; however, I'm not a fan of PHP either. I like the new pipe syntax as a concept, but when added to an already uncomfortable overall programming environment, it can only provide mild relief.
- rambambram 1y agoYou mean the fourth decade of PHP.
- oblio 1y agoI'm not a fan of it, but JavaScript will outlive me.
- hajile 1y agoI'd rather write JS/TS than most of the other popular languages.
- ioma8 1y agoThe syntax is ugly as hell.
- frankzander 1y agoAmen ... I mean PHP could have been such a good language if the syntax wouldn't be such a show stopper.
- JohnKemeny 1y agoThank you for your insight.
- deleted 1y ago[deleted]
- someothherguyy 1y agocomposition would be much nicer than this, maybe soon
- moebrowne 1y agoMight be sooner than you think. There are already RFCs for Partial Function Application and Function Composition: https://wiki.php.net/rfc/partial_function_application_v2 https://wiki.php.net/rfc/partial_function_application_v2 https://wiki.php.net/rfc/function-composition https://wiki.php.net/rfc/function-composition
- mappu 1y agoEvery single one of those steps buffers into a temporary variable - this isn't efficient like a bash pipe.
- quietbritishjim 1y agoGenuine question from a non-PHP user: Does PHP support iterator-like objects? Like Python I mean, where mydict.values() produces values on demand, not immediately realised as a list. Or are all steps necessarily guaranteed to be fully realised into a complete list?
- severak_cz 1y agoyes, for a long time - https://www.php.net/manual/en/class.iterator.php https://www.php.net/manual/en/class.iterator.php
- quietbritishjim 1y agoInteresting, but I suppose I was particularly interested if that's what's actually happening with the transformations in the example in the article. Are those making use of this protocol? The comment I originally replied to seems to imply they aren't.
- throw_m239339 1y agoPHP does have generators and iterators yes, although I personally rarely use them directly.
- Timwi 1y agoThe section where the article mentions function composition implies that it doesn't. The article says that compositing the functions before passing them into map would be an optimization. I take that to mean that without the composition, each map fully processes an array passed to it from the previous map, and the first map fully reads the whole file in the example. If it were iterable, the function composition would make no difference compared to a pipeline of multiple maps. Meanwhile, I'm confused as to why it sometimes says “map” and sometimes “array_map”. The latter is what I'm familiar with and I know that it operates on a whole array with no lazy evaluation. If “map” isn't just a shorthand and actually creates a lazy-evaluated iterable, then I'm confused as to why the function composition would make any difference.
- dev_l1x_be 1y agoRust is next? Jokes aside, pipe operators in programming languages have a interesting side effect of enabling railway oriented programming that I miss the most when not working in F#.
- simonask 1y agoThe way function/trait resolution works in Rust, it's actually already quite idiomatic to code in this style (just using the dot operator). The standard library Iterator is a great example of this. :-) I don't think there's any significant push for an even terser syntax at the moment.
- realharo 1y agoThere is the `tap` crate (https://crates.io/crates/tap https://crates.io/crates/tap) which adds `tap`, `pipe` and their variants to everything.
- pknerd 1y agoAm I the only one who found it ugly?
- defraudbah 1y agoPHP is that weird beast that no one wants to praise and yet it works tremendously well for those who manage to tame it. I would likely never touch it as there are too many languages to use and what I know is more than enough to do my job, but I am super excited to see languages like PHP that aren't mainstream in my bubble to keep evolving
- nolok 1y agoI'm not tempting you do to it or anything, but I want to say given your point of view, if one day you need a crude+ app and try to do it using laravel, you might be really surprised by what modern php actually is. There was a point were I thought the language and it ecosystem was going down the drain but then they recovered and modern php is 90% what do you want to do and don't worry about the how, it's easy. I don't use it much anymore, but every time I do all I see are possibilities.
- defraudbah 1y agowhat about deployment? I assume I need to scp files like Python or keep everything in a single giant PHP file? is that an option?
- nolok 1y agoDeployment these days is essentially git pull && composer update Of course not if you use vm or serverless or whatever like this, but for a basic here is my crude app, that's what you do. Or if you want to go old school sure, just scp that directory, it still works like it did 30 years ago.
- defraudbah 1y agoawesome, thank you
- claar 1y agoLaravel Forge handles auto-deployment on push to master. Or if you want production zero downtime deployments, use Laravel Envoyer.
- BiteCode_dev 1y agoIt's lovely to see how PHP keeps growing. It's far from what it was when I used to code with it in V3. I really thought it would be lost in its bad design, but the core devs kept at it, and it is, indeed, a pretty decent language now.
- cpursley 1y agoYour move, JavaScript.
- mort96 1y agoI'm surprised that the example requires lambdas... What's the purpose of the `|> foo(...)' syntax if the function has to take exactly one operand? Why is it necessary to write this? $arr |> fn($x) => array_column($x, 'tags') Why doesn't this work? $arr |> array_column(..., 'tags') And when that doesn't work, why doesn't this work? $arr |> array_unique
- tossandthrow 1y agoIt is to interject the chained value at the right position in the function. They write that elixir has a slightly fancier version, it is likely around this, they mean (where elixir has first class support for arity > 1 functions)
- ptx 1y agoApparently "foo(...)" is just the PHP syntax for a function reference, according to the "first-class callable" RFC [1] linked from the article. So where in Python you would say e.g. callbacks = [f, g] PHP requires the syntax $callbacks = [f(...), g(...)]; As for the purpose of the feature as a whole, although it seems like it could be replaced with function composition as mentioned at the end of the article, and the function composition could be implemented with a utility function instead of dedicated syntax, the advantage of adding these operators is apparently [2] performance (fewer function calls) and facilitating static type-checking. [1] https://wiki.php.net/rfc/first_class_callable_syntax https://wiki.php.net/rfc/first_class_callable_syntax [2] https://wiki.php.net/rfc/function-composition#why_in_the_engine https://wiki.php.net/rfc/function-composition#why_in_the_eng...
- zelphirkalt 1y agoHm. Looks like PHP actually got a modern feature there, and it is looking decent, not like the usual new PHP feature, that just looks worse than in other languages, where it has been standard. Consider me surprised, that they seem to have done a good job on this one. And they even dodged the bullet with making the right side callables, which avoids the trap of inventing new types of expressions and then not covering all cases.
- rogue7 1y agoThis looks neat. However since I read about Koka's dot selection [0], I keep thinking that this is an even neater syntax: fun showit( s : string ) s.encode(3).count.println However, this is of course impossible to implement in most languages as the dot is already meaningful for something else. [0] https://koka-lang.github.io/koka/doc/book.html#sec-dot https://koka-lang.github.io/koka/doc/book.html#sec-dot
- throw-the-towel 1y agoI think this is called uniform function call syntax.
- jprafael 1y agoThat syntax is very clean when it works. I think however the limitation of not being able to pipe arguments into 2nd, 3rd, ..., positions and keyword arguments, or variadic explosion like the syntax showcased in the article makes it less powerful. Are there other syntax helpers in that language to overcome this?
- account42 1y agoIt still makes sense to have a clean syntax for the simple case. You can use currying (with or without first class language support) to handle more complex cases or just fall back to good old function composition or even loops.
- btbytes 1y agoIt is called Uniform [Function] Call Syntax. D has had this for decade(s): https://tour.dlang.org/tour/en/gems/uniform-function-call-syntax-ufcs https://tour.dlang.org/tour/en/gems/uniform-function-call-sy... Nim too has it: https://nim-by-example.github.io/oop/ https://nim-by-example.github.io/oop/
- ds_ 1y agoOne of the many joys of working with Clojure https://clojure.org/guides/threading_macros https://clojure.org/guides/threading_macros
- mhh__ 1y agoEvery language should have this. Forget about transforming existing code, it makes new code much more reasonable (the urge to come up with OOPslop is much weaker when functions are trivial) — they're programming languages for a reason.
- xorcist 1y ago"Essentially the same thing" as a shell pipe, except each function run sequentially in full, keeping output in a variable. So nothing like a shell pipe. For short constructions '$out = sort(fn($in)' is really easier to read. For longer you can break them up in multiple lines. $_ = fn_a($in) $_ = fb_b($_) $out = fn_c($_) Is it really "cognitive overhead" to have the temporary variable explicit? Being explicit can be a virtue. Readability matters in a programming language. If nothing else I think Python taught us that. I am skeptical to these types of sugar. Often what you really want is an iterator. The ability to hide that need carries clear risk.
- scotty79 1y agoI though so too, but if you are using same name for various things then the whole thing can't be typechecked. Not in PHP at least. In Rust this would work perfectly with typechecking.
- lihaciudaniel 1y agoWow dead language adds a special letter wowooeoowowoowoo
- sumeetdas 1y agoThe first typed programming language where I've seen pipe operator |> in action was in F#. You can write something like: sum 1 2 |> multiply 3 and it works because |> pushes the output of the left expression as the last parameter into the right-hand function. multiply has to be defined as: let multiply b c = b \* c so that b becomes 3, and c receives the result of sum 1 2. RHS can also be a lambda too: sum 1 2 |> (fun x -> multiply 3 x) |> is not a syntactic sugar but is actually defined in the standard library as: let (|>) x f = f x For function composition, F# provides >> (forward composition) and << (backward composition), defined respectively as: let (>>) f g x = g (f x) let (<<) f g x = f (g x) We can use them to build reusable composed functions: let add1 x = x + 1 let multiply2 x = x \* 2 let composed = add1 >> multiply2 F# is a beautiful language. Sad that M$ stopped investing into this language long back and there's not much interest in (typed) functional programming languages in general.
- christophilus 1y agoF# is excellent. It’s tooling, ecosystem, and compile times are the reason I don’t use it. I learned it alongside OCaml, and OCaml’s compilation speed spoiled me. It is indeed a shame that F# never became a first class citizen.
- cosmos64 1y agoLots of this, especially the tooling and ecosystem, improved considerably in the last couple of years. OCaml is a great language, as are others in the ML family. Isabelle is the first language that has introduced the |> pipe character, I think.
- dmead 1y agoHaskell seems pretty dead as well. Good think php has another option for line noise though.
- gylterud 1y agoWhat makes you believe Haskell is dead or even dying? New versions of GHC are coming out, and in my experience, developing Haskell has never been smoother (that’s not to say it is completely smooth).
- elric 1y agoMakes for a fun programming paradigm, similar to Java's streams-with-lambdas. Great for readability. Not too fond of the |> operator though, requires 4 different keypresses on my keyboard layout, not terribly ergonomic. But I understand that options were limited and it is sort of clear.
- major505 1y agoSo php now can do clojure like programing?
- donatj 1y agoI had this argument in the PHP community when the feature was being discussed, but I think the syntax is much more complicated to read, requiring backtracking to understand. It might be easier to write. Imagine you're just scanning code you're unfamiliar with trying to identify the symbols. Make sense of inputs and outputs, and you come to something as follows. $result = $arr |> fn($x) => array_column($x, 'values') |> fn($x) => array_merge(...$x) |> fn($x) => array_reduce($x, fn($carry, $item) => $carry + $item, 0) |> fn($x) => str_repeat('x', $x); Look at this operation imaging your reading a big section of code you didn't write. This is embedded within hundreds or thousands of lines. Try to just make sense of what "result" is here? Do your eyes immediately shoot to its final line to get the return type? My initial desire is to know what $result is generally speaking, before I decide if I want to dive into its derivation. It's a string. To find that out though, you have to skip all the way to the final line to understand what the type of $result is. When you're just making sense of code, it's far more about the destination than the path to get there, and understanding these require you to read them backwards. Call me old fashioned, I guess, but the self-documentating nature of a couple variables defining what things are or are doing seems important to writing maintainable code and lowering the maintainers' cognitive load. $values = array_merge(...array_column($arr, 'values')); $total = array_reduce($values, fn($carry, $item) => $carry + $item, 0); $result = str_repeat('x', $x);
- sandbags 1y agoI don’t disagree with your reasoning but I would have thought this pipe would be in an appropriately named function (at least that’s how I’d use it in Elixir) to help understand the result.
- philjohn 1y agoThis is what a good IDE brings to the table, it'll show that $result is of type string. The pipe operator (including T_BLING) was one of the few things I enjoyed when writing Hack at Meta.
- xienze 1y ago> This is what a good IDE brings to the table, it'll show that $result is of type string. I think the parent is referring to what the result _means_, rather than its type. Functional programming can, at times, obfuscate meaning a bit compared to good ol’ imperative style.
- penguin_booze 1y agoI don't imagine it's widely known (which I completely understand): vimscript has an arrow operator with similar piping effect, a la foo->bar(baz)->qux() . See the doc: https://vimhelp.org/eval.txt.html#method https://vimhelp.org/eval.txt.html#method.
- mg 1y agoI'm confused about the rationale behind: |> fn($x) => array_column($x, 'tags') Why is that inlined function necessary? Why not just |> array_column(..., 'tags') ? I mean, I understand that it is because the way this operator was designed. But why?
- rafark 1y ago> |> array_column(..., 'tags') This syntax is invalid. But it will be possible next year with the proposed partial function application rfc array_column(?, 'tags') https://wiki.php.net/rfc/partial_function_application_v2 https://wiki.php.net/rfc/partial_function_application_v2
- LorenDB 1y agoReminds me of D's Uniform Function Call Syntax[0], which allows you to rewrite bar(foo(sort(myArray))) as myArray.sort().foo().bar(). The difference is that D allows extra function arguments, keeping the passed-in value as the first argument. So you could have myArray.sort().writeln("extra text"), for example. [0]: https://tour.dlang.org/tour/en/gems/uniform-function-call-syntax-ufcs https://tour.dlang.org/tour/en/gems/uniform-function-call-sy...
- jcmontx 1y agoVery nice, great F# feature, hope to see it in many other languages!
- chuck8088 1y agoThis article makes a great case WHY the pipe operator is useful, but why didn't they just rewrite those functions to support method chaining? ` $profit = [1, 4, 5] .loadSeveral() .filter(isOnSale()) .map(sellWidget()) .array_sum(); ` this has the side benefit of 'looking normal'
- wsatb 1y agoBackwards compatibility. The language has done a pretty amazing job at adding features over the last 10 years without really breaking a lot of old code. I believe PHP still runs about 75% of the internet, so that's pretty huge.
- moebrowne 1y agoThe Python 2 to 3 upgrade is a example of how important backwards compatibility is
- troupo 1y agoBecause pipes work on all functions, not just object methods. So your business logic, validations etc. don't have to be methods of the built-in objects. And there's nothing abnormal about pipes
- Mystery-Machine 1y agoPHP: $result = $arr |> fn($x) => array_column($x, 'tags') // Gets an array of arrays |> fn($x) => array_merge(...$x) // Flatten into one big array |> array_unique(...) // Remove duplicates |> array_values(...) // Reindex the array. ; // <- wtf Ruby: result = arr.uniq.flatten.map(&:tags) I understand this is not pipe operator, but just look at that character difference across these two languages. // <- wtf This comment was my $0.02.
- Alifatisk 1y agoCan't the pipe operator be easily mimicked in Ruby thanks to its flexibility? I'm thinking of something like this: class Object def |>(fn) fn.call(self) end end which then can be in the following way: result = arr |> ->(a) { a.uniq } |> ->(a) { a.flatten } |> ->(a) { a.map(&:tags) } Or if we just created an alias for then #then method: class Object alias_method :|>, :then end then it can be used like in this way: arr |> :uniq.to_proc |> :flatten.to_proc |> ->(a) { a.map(&:tags) }
- rafark 1y agoThe great thing about this pipe operator is that it accepts any callable expression. I’m writing a library to make these array and string functions more expressive. For example, in php 8.5 you’ll be able to do: [1,1,2,3,2] |> unique And then define “unique” as a constant with a callback assigned to it, roughly like: const unique = static fn(array $array) : array => array_unique($array); Much better.
- moebrowne 1y agoThe trailing semi-colon on a new line helps prevent Git conflicts and gives cleaner diffs. It's the same reason PHP allows trailing commas in all lists.
- daneel_w 1y agoPutting the delimiter on a line of its own is a syntactical trick that helps bringing small additions down to a neater 1-line diff instead of a 2-line diff. You've probably run into it many times before in other contexts without thinking of it. Arrays/hashes, quoted multi-line strings etc.
- jadbox 1y agoNow if only JS or Typescript can jump on this ship!
- scop 1y agoThis is great! Hat tip to PHP. I first came across pipes in Elixir and have ever since missed it in every other language. Two observations: - pipes make you realize how much song and dance you do for something quite simple. Nesting, interstitial variables, etc all obscuring what is in effect and very orderly set of operations. - pipes really do have to be a first class operator of the language. I’ve tried using some pipe-like syntactic sugar in languages without pipes and while it does the job, a lot of elegance and simplicity is lost. It feels like you are using a roundabout thing and thus, in the end, doesn’t really achieve the same level of simplicity. Things can get very deranged if you are using a language in a way it wasn’t designed for and even though I love pipes I’ve seen “fake pipes” make things more complicated in languages without them.
- elif 1y agoI used php professionally for a decade and I still don't get why in the year 2025 we need to reinvent syntax that is virtually standard in every language
- dagi3d 1y agoI wish they reconsider it again i ruby
- adius 1y agoPHP getting a pipe operator with ways to implement a Maybe Monad was definitely not on my 2025 bingo card. But any changes making mainstream languages more functional are highly welcome! It’s just more ergonomic than imperative code.
- flufluflufluffy 1y agoIt’s cool. Personally I probably won’t be using it though. I think a few temp variables or dedicated functions to do some computation that takes more than 2 or 3 iterated operations is better for readability and maintainability.
- mannyv 1y agoSo, what happens if a call in the middle fails?