Less Noisy Composition with Operators

  • by

Photo by Dean Brierley on Unsplash

There is no such thing as free lunch. In programming, there is no such thing as the best choice. We are constantly making trade-offs. And there is always a price to pay. Some times it’s speed at other times it’s efficiency and at other times simplicity.

Taking apart reality

How to translate a real life thing to something that a computer can understand? There are some steps that need to be taken to strip anything that is not essential to the problem. Thus making a concrete thing more abstract so at the end we are working with a pure idea.

Let say you see a chair or you are imagining one. Now you are asked to draw it or draw its representation. How far can you go with that? This is how one thing goes from a concrete implementation to an idea of something to sit on.

Chatty API

Let’s take a look at where we left off last time:

// (x + 2)^4 + 3
let composed =
compose(           // <-- plumbing🚰 (x + 2)^4 + 3
    compose(       // <-- plumbing🚰 (x + 2)^4
        compose(   // <-- plumbing🚰  x + 2
            incr,  // <-- basic block 🧱 +1
            incr   // <-- basic block 🧱 +1
        ),
        compose(   // <-- plumbing🚰 ^4
            multi, // <-- basic block 🧱 ^2
            multi  // <-- basic block 🧱 ^2
        )
    ),
    compose(      // <-- plumbing🚰 +3
        compose(  // <-- plumbing🚰 +2
            incr, // <-- basic block 🧱 +1
            incr  // <-- basic block 🧱 +1
        ),
        incr      // <-- basic block 🧱 +1
    )
)

composed(-1)   == final(-1)   // true
composed(zero) == final(zero) // true
composed(1)    == final(1)    // true

If you have an eye that is accustomed to reading this structure (which is not so complicated!) then it looks ok. But let’s face it. It’s a chatty API.

Operators

I know that operators have their reputation and everyone has an opinion about them. But hey they are here and can give something that plain words cannot and in daily life, there are a ton of operators that is in use already. Just take a look at road signs or arrows in navigation systems that point you in the right direction. Yes, you have to learn them but when you do life without them would be not so comfortable.

Keeping it low

As we saw composition is very cool and enables ease of gluing different parts to make something new. But man… it’s loud! I guess if you like it then this is just fine. Some music should be played loud. But I would like it if all of that plumbing would be hidden. Don’t be fueled it’s there but it will not obstruct the view of what is important.

Side Note

Inspiration for those operators is Swift Prelude which is maintained by the PointFree guys. I definitely encourage you to take a look at their work on GitHub and on their site.

Pipe Forward |>

precedencegroup ForwardApplication {
    associativity: left
}

infix operator |>: ForwardApplication

public func |> <A, B>(x: A, f: (A) -> B) -> B {
    f(x)
}

There are three parts to this code snippet. First is the language construct that describes how to execute this operator when other operators are present. That way Swift will know how to handle precedence and if the same operator is used in the same expression.

Next comes the definition of the operator. You must tell the compiler where it’s allowed to use this operator. Is it in the beginning, between, or at the end of operands. One more piece of information is required and that’s the precedence group this operator belongs to.

Finally, it’s time to write down the actual implementation of the operator. And I hope it comes with no surprise that it’s just a function!

How does it work?

If you have any experience with shell in the terminal then you will be glad to know that it’s the same as the pipe operator. So it will take a value from the left and pipe it to a function that is on the right. The shape of this operator is also similar like on other functional programming languages eg. F#.

1 |> incr |> { x in x * x } |> incr 

1
    |> incr
    |> { $0 * $0 }
    |> incr

Time to take things slowly. At first sight it might look weird but let’s look at it piece by piece.

At the begging expression starts with a value of 1. This is piped to a function that is called incr. When this function finishes it’s computation it returns another value. This is then piped again to an anonymous function defined inline! When this block finishes the result is piped yet another time to incr function.

There is a lot of freedom on how to write those pieces that go to this pipeline. Right to left or top to bottom. Either way, it’s focused on the essentials of the computation, not the extra bits that are required to pass values around.

Summary

As always please do check out the content on links that I have put in this post. It will worth your while! If you want one more cool library with operators then take a look at Runes.

Leave a Reply

Your email address will not be published. Required fields are marked *