Composable parts

Summary: Composition is an important idea in programming, and Functional Programming brings it to the forefront. But what does it mean to say things are composable?

You've heard that composition or composability is very highly valued in functional programming. But what does it mean to be composable? And why is it important?

Composability is generally compared to legos. Legos are very composable. They are all engineered to a very high precision so that any two parts can fit together. They fit together in a certain way, and all the sizes are right, so that when you're building from two opposite sides, the two sides will meet in the middle. The pieces are designed to compose. You have a bunch of bricks that fit together, and while they all work together to build a structure, they are all still separate and decomposable. That is, you can take it back apart.

The Lego interface

The Lego interface

Why do you care if you can take it apart? Well, if you made a mistake building it, or somehow the thing needs to change (changing requirements?), you can pull out some bricks and build it again. Maybe you completely rebuild it. Even if you had to start over, at least you could reuse the bricks.

The same works for software. Composable parts of software work well together. They can be reused in different combinations. When something needs to change, you don't have to start over. You can just pull the parts apart and put them back together in a new way. Software maintenance is easy. New features are easy. A rewrite is easy. Well, that's the ideal, anyway.

As far as metaphors go, it's pretty good. People are familiar with Legos and they are reusable components. But there's another side to composition that is often not talked about. It's very hard to talk about composition without talking about decomposition. They're two sides of the same coin. And while composing Legos is easy, I can only imagine designing new Lego bricks is very difficult. That metaphor extends to software, too. Putting composable software parts together is easy. But designing the parts in the first place is hard. And it's all about decomposition.

Here's a story: Big Toy Co makes a really cool pirate ship that is a single piece of molded plastic. It's very cheap to make, once you have the mold. It took someone a long time to make the pirate ship mold. The company paid for that work and it's paying off. The toy sells really well.

Constructo, Inc. makes pirate ships, too. Theirs is made of hundreds of small parts. You spend hours putting it together before you can play with it. It's selling pretty well, too. In fact, both pirate ships are selling about the same.

Then something happens. Pirate ships go out of style faster than you can say "Aye, aye, captain!". What's cool now is spaceships! Both companies scramble to make a spaceship toy. Big Toy Co starts designing a completely new mold to make their toy spaceship. It's fast tracked through R&D, but they estimate it will take 6 months to roll out.

But the designers at Constructo, Inc realize that most of their parts would be useful in a new spaceship toy. They've got simple shapes like rectangles and squares. They just need to make one or two new pieces and they can build a spaceship out of what they have. They quickly design those pieces and begin shipping new spaceship kits within three weeks. They win the race to the toy stores and kids buy them. Meanwhile, the first company is still in the design phase.

Why were they able to move so much faster? Because of all of the investment in reusable components.

Look at the Javadoc for a javax.servlet.ServletRequest. Here is an excerpt of the methods: ^1

    getAttribute(String s)
    getParameter(String s)

Trust me, there's a lot to an HTTP request.

Now, here's an excerpted Ring request. (I've left out most of the headers.)

    {:server-port 80
     :server-name ""
     :remote-addr ""
     :uri "/index.html"
     :query-string "?hello=true"
     :query-params {"hello" "true"}
     :scheme :https.
     :request-method :get
     :headers {"accept" "text/html"}}

My point in showing this was not to say, look, Ring's is shorter. My point is that Ring is made of reusable parts. First, it's a hashmap. The Servlet is ostensibly a hashmap, because it has that getAttribute() method that takes a String and returns an Object. So it's no better. But instead of just making it a hashmap and simply reusing what was already there, what already worked, what was familiar, and what has lots of functions that would work with it already, they invented a whole new thing. It's like having to make a whole new mold from scratch each time you want to produce a new toy.

What the designers of Ring did was to decompose the problem, analyze its properties, and find existing parts that had those properties. "HTTP headers? Well, they're just String keys to String values, per the HTTP spec. That would be perfectly represented by a hashmap." etc, etc. And when it is a hashmap, you can use all of the existing functionality of hashmap for free. Let me say that again: the headers hashmap works with all code that works with hashmaps: standard library code, your existing code, external libraries, and all future code.

The Ring interface

The Ring interface

There's a lot more to say about composition and composable parts. But this is getting long now, so I'll wrap it up. Just know that in Clojure, the standard library is full of these kinds of parts. They do one small thing, they do it well, and they have a standard interface. Like Lego bricks. Sometimes you can build your entire system out of the existing parts in core.

This aesthetic of building reusable components pervades the Clojure community. Not everything works this way, but a lot does. So very often you'll find you can use someone else's library. And sometimes you need to make new parts yourself. If you make them small and reusable, they will serve you well for a long time.

If you like this line of thinking and want to learn more about how you can build composable parts, you should join the Online Mentoring Program. It's a step-by-step guide to learning professional Clojure, delivered with video lessons, screencasts, exercises, and more.

  1. Thanks to Rich Hickey for this great example.