Hacker News new | past | comments | ask | show | jobs | submit login
PHP Pipes in 156 Bytes (github.com/bouldersharp)
32 points by bouldersharp on March 3, 2022 | hide | past | favorite | 30 comments



    function slug(string $s) {
        return pipe($s, 
          fn($s) => strtolower($s), 
          fn($s) => str_replace(" ", "-", $s)
        );
    }

    slug("10 WAYS to EAT more HEALTHY"); // 10-ways-to-eat-more-healthy
I'm not sure I see how this example demonstrates the point of something like this.

Surely this is simpler and more succinct?

    function slug(string $s) {
        return str_replace(" ", "-", strtolower($s));
    }
Even if you wanted to break it up into two lines:

    function slug(string $s) {
        $s = strtolower($s);
        return str_replace(" ", "-", $s);
    }
A better example might go a long way here.

Edit: Never walk away to refill a coffee before pressing submit or the rest of HN will beat you to it. :P


> A better example might go a long way here.

I am a Bad Programmer (it's not my day job, though I have managed programmers for many years), so maybe I am just missing the point in examples like this - but I often find this is the case in many code samples for language features. They usually have a trite example that shows how it can work in a really basic case.

This is helpful from a purely technical level - as here, I can see what is going on neatly.

But they rarely seem to show it being used how the designer expects it to be used in the wild to save time, or increase productivity, or whatever. Like, why would anyone want to do this?

I would personally NEVER want to see a function like that in a codebase when it could be written so much more clearly as in your examples. Although I personally would prefer a third style:

  function slug(string $s) {
        $s = strtolower($s);
        $s = str_replace(" ", "-", $s);
        return $s;
  }
... more verbose, but I never see the point in trying to save a line if it sacrifices even a small bit of readability. Again, as a Bad Programmer, I like things to be spelled out super clearly whenever possible, so maybe that's just me.


This. I too am a bad programmer and readability is so much more important to me than the LOC count.

Sure, the pipes example might look nifty, but from a practical viewpoint, it's just not that useful. Next thing you know, someone throws an eval into the mix by mistake, and we end up with unintended consequences.


Also your preference is faster.


I guess that's a good way to make code very... unreadable and hard to set breakpoints on? What's wrong with:

    function slug(string $s) {
      $s = strtolower($s);
      $s = str_replace(" ", "-", $s);

      return $s;
    }
And, if you need classes, aren't you better served with a Decorator implementation? I just don't see the point here.


You (bouldersharp) already posted this 2 weeks ago. You seem to be very proud about this. But can you explain what the big deal is? I don't understand why anyone would want to use it.


Presumably this is meant to be a (very approximate) userland-implementation of the declined Pipe Operator RFC[0].

[0]: https://wiki.php.net/rfc/pipe-operator-v2


One use case for this would be unstacking a bunch of array_* functions since the nesting can start getting to be a little dense. Here's a common function I run into a lot:

    function cleanIdsTraditionally(array $input): array
    {
        return array_unique(array_filter(array_map('intval', $input)));
    }
Here it is as the piped version:

    function cleanIdsWithPipe(array $input): array
    {
        return pipe($input,
            fn($input) => array_map('intval', $input),
            fn($input) => array_filter($input),
            fn($input) => array_unique($input)
        );
    }
Here, I think the added context of the pipe negates the enhanced readability of having each method on its own line. Anything more complex than this would benefit from being piped, but at that point, it would probably be better to break the steps down into more discrete functions anyways.

Overall, not a bad idea, but in practice, it would be hard to justify having around


While that is correct, I would definetely prefer using the syntax used in Symfony ArrayCollection[1]:

  $collection = new ArrayCollection([4,3,2,1]);
  $collection->filter(fn($element) => $element % 2 == 0)
             ->map(fn($element) => $element+2)
             ->toArray();
             
It is pretty much like in JavaScript or C# with Linq, but with less features.

But you can implement this yourself using

  - ArrayInterface, Traversable, Countable, etc.
  - generators / iterators (yield)
It's not that hard. There is also YaLinqo[2]

[1] https://www.doctrine-project.org/projects/doctrine-collectio... [2] https://github.com/Athari/YaLinqo


Very useful for more complex situations. Thanks for this


Not too long ago I made something very similar but subtly different:

    function pipes(callable ...$pipes): Closure
    {
        return function($value) use ($pipes) {
            foreach ($pipes as $pipe) {
                $value = call_user_func($pipe, $value);
            }
 
            return $value;
        };
    }
This makes the pipes reusable and makes it so that functions that take only a single argument can be passed as a plain string:

    $sanitize = pipes('trim', 'htmlspecialchars');

    $title = $sanitize($title);
    $body = $sanitize($body);
While also preserving the ability to pass a closure when you need multiple arguments.


What is the benefit of this?

Right now I see an example of how to write unreadable and hard-to-debug code

Being a senior PHP dev I would not let pass such code into our master branch, mainly because it is syntactic sugar that does not help with a specific problem and also does not attribute to a clearer and readable code at all.

Remember: In a company, where we rely on the code to earn our living the two factors readbilty and testabilty are the single most important ones. Because if you are off to holiday and your coworkers can't get a grasp of your (seemingly) clever code, than the whole company has a problem.


Here's my version:

    function pipe($callables) {
        return array_reduce($callables, fn($carry, $callable) => call_user_func($callable, $carry));
    }
Whenever a method merely calls other methods in sequence, returning the result of the last call, it's just too easy for me to simply use a pipe function. I guess it's mostly a matter of preference.


Ceci n’est pas une pipe.


Topical.


Wrote a python pipe operator after falling in love with elixir just the other day:

https://gist.github.com/runekaagaard/5a172eccfa2fdbcd625a3a0...


Why wrapped in a class? Seems entirely redundant or am I missing something?


PHP can't autoload functions, only classes. Thus if you wrap it in a static function inside a class you can autoload the the function by calling the class, Pipe::new(...), without doing a require "file.php".


You can add a file import to composer, which is built into vendor/autoload.php automatically. I suppose this allows you to load this as a package...


That's not done lazily which is considered bad


That's what I was wondering too, especially considering there is a helper function that just wraps it. Seems rather unnecessary.

   <?php
   
   function pipe(...$args) {
     // Retrive first argument as initial value
     $value = array_shift($args);
   
     // Call the functions passing $value and assign its return
     foreach($args as $func) $value = $func($value);
   
     // Return computed value
     return $value;
   }
   
   echo pipe("10 WAYS to EAT more HEALTHY", 
     fn($s) => strtolower($s), 
     fn($s) => str_replace(" ", "-", $s)
   );
Works just as well.


Passing the first argument as part of the variadic array isn't necessary.

Here's my version in only 66 bytes, what do I win? ;)

  <?php function pipe($v,...$p){foreach($p as$f)$v=$f($v);return$v;}


64! (if you don't mind calling a variable).

    <?php $pipe=fn($v,...$p)=>array_reduce($p,fn($a,$f)=>$f($a),$v);


I guess I was just removing the need for an unnecessary class, but that's fair.


Perhaps you could extend the Pipe class and thus the pipe() method returns an object for chainability? That's the only reason I can immediately think of.


free functions scary



Why is there a .min version? Did the author confuse it with JavaScript?


Hahaha, that just sums up the ridiculous nature of that piece of art. I guess this is an April fools joke gone wrong.


"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." - Brian Kernighan




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: