DEV Community

lray138
lray138

Posted on

Going Mad With Monads

Years ago now... sometime back in 2018, or 2019, I bought a book called "Pro PHP Functional Programming".

Functional programming was all the rage back then, and so I decided to learn in a language I was most familiar with.

At some point deep in the book the author mentioned NOT recommending using Monads in PHP. First, the whole point of the book, in my mind, was to learn about "The Mysterious Monad", and 2nd, the one way to get me to do something is tell me not to do it. Thus began the great misadventure I pray is finally coming to an end.

I had also read a book by Christopher Pitt "Typed PHP" and combining those ideas, with my desire to utilize these functional techniques in my content management job, led me to go down all sorts of rabbit holes in search of the "ultimate toolbox" or "ultimate approach" ... the deeper understanding that would make me that mythical 10x-ninja badass and then I could go back to focusing on music while my system starting bring in passive income.

Light backstory aside, I am coming here because I am attempting to document the final stages of this package I have been putting together in the hopes that my work might be of some value to someone somewhere in the near future. Hopefully that person is myself.

Ha.

So, I'm actually refactoring some code based on a previous iteration of this content management layer I've been working on, where I mistakenly thought it would be cool to give everyting a bind method and make types obey the monadic interface.

After the ADHD burnout recharge, I came back and realized I was a little off.

Finally, it clicked. I needed a "pipe" method that would allow me to continue method chaining, thus creating a nice pipeline.

So.. Here "all_images" is a collection/list (Lst) of images coming from a WordPress instance. We take those images and map partial components on the data and reduce it to a Str (again influenced by Christopher Pitt's Typed PHP book).

Since the "inc" function returns a Str type object, in my mind using bind made sense (at the time). But I have since realized the error of my ways and removed it, which led to the dreaded Fatal Error. Luckily, I had done some deep thinking with the aid of ChatGPT and implemented a "pipe" method that would allow me access to the whole object, and the best part is my return type can be whatever I want, not that it matters in this example.

So...

$all_images = $all_images
    ->map(fn($x) => inc("partials/masonry-gallery/card-wrap")([
        "content" => inc("partials/masonry-gallery/card")($x),
        "image" => $x['img_src'],
    ]))
    ->reduce(fn(Str $acc, $x) => $acc->append($x), Str::of(""))
    ->bind(fn($x) => inc("partials/masonry-gallery/section")([
        "content" => $x
    ]))
    ;
Enter fullscreen mode Exit fullscreen mode

becomes

$all_images = $all_images
    ->map(fn($x) => inc("partials/masonry-gallery/card-wrap")([
        "content" => inc("partials/masonry-gallery/card")($x),
        "image" => $x['img_src'],
    ]))
    ->reduce(fn(Str $acc, $x) => $acc->append($x), Str::of(""))
    ->pipe(fn($x) => inc("partials/masonry-gallery/section")([
        "content" => $x
    ]))
    ;
Enter fullscreen mode Exit fullscreen mode

alternatively I could map by extracting the return of the inc function:

$all_images = $all_images
    ->map(fn($x) => inc("partials/masonry-gallery/card-wrap")([
        "content" => inc("partials/masonry-gallery/card")($x),
        "image" => $x['img_src'],
    ]))
    ->reduce(fn(Str $acc, $x) => $acc->append($x), Str::of(""))
    ->map(fn($x) => inc("partials/masonry-gallery/section")([
        "content" => $x
    ])->get()
    ;
Enter fullscreen mode Exit fullscreen mode

I do think it helps readability to provide the type in the function signature. Since being a "lazy programmer" is considered good I don't always do that.

$all_images = $all_images
    ->map(fn($x) => inc("partials/masonry-gallery/card-wrap")([
        "content" => inc("partials/masonry-gallery/card")($x),
        "image" => $x['img_src'],
    ]))
    ->reduce(fn(Str $acc, $x) => $acc->append($x), Str::of(""))
    ->pipe(fn(Str $x): Str => inc("partials/masonry-gallery/section")([
        "content" => $x
    ]))
    ;
Enter fullscreen mode Exit fullscreen mode

So, going mad with Monads, and we don't even use a monad?

Yes, exactly.

Top comments (0)