What is the closure property?
I discuss the closure property, which creates operations that can be nested. It's one thing that makes an API feel like a DSL.
[00:00:00] What is the closure property and why is it important?
[00:00:04] Hi, my name is Eric Normand, and this is my podcast. Welcome.
[00:00:11] So in level three, we're talking about algebraic modeling. One of the big ideas that helps make a model more expressive is the closure property. Closure property is very simple, and you're probably familiar with it from other code you've written before, but it basically says that the return type is the same as the argument type.
[00:00:39] If you have a binary function, usually that means the two arguments are the same type as the return type. We see this a lot. Examples are addition takes two numbers, returns a number. Multiplication, takes two numbers, returns a number .
[00:00:56] What does that let you do? It lets you nest. You [00:01:00] can nest multiplications together to create very complex expressions. You don't have to do them step by step. If you have a piece of code where your data model doesn't have the closure property, often what it means is you'll do some operation on a value, and then you get a thing of a different type.
[00:01:24] And then you do something on that one and you get a thing of a different type, and then you do something on that one, you get a thing of a different type. Now that can express exactly what you need, just fine. But notice it's linear. If you want to add steps or you want to make it more complicated, mostly the only thing you can do is to add more steps somewhere in that flow.
[00:01:49] It just grows in one dimension. Whereas if you have a set of operations that have the closure property, that means you have this [00:02:00] new dimension of nesting. Stuff can get more deeply nested inside of the expression. Which means that basically you just have this other dimension to work with for expressivity.
[00:02:18] When I can, I look for a way of making something have the closure property. Often there's a few patterns that you can look for to see how to do it. One is to look for combining operations. The standard way of making a hash map is you have an empty one, and then you add a key and you add a key, and you add a key, or you have some literal that just has it all in one thing, right? But then after you've made the literal, then you just add keys, or move keys, et cetera. There's [00:03:00] also this operation called Merge. Now look at Merge. It takes two hash maps and it returns a new hash map. It has the closure property. This lets you nest your expressions in another dimension besides just linearly adding and removing individual keys.
[00:03:24] Another example is averaging. So the standard algorithm for calculating the average is to sum up all the numbers and you divide it by the count of the numbers . That'll get you the answer. But notice at no point were we talking about average until the end. So the return value is an average, but the individual things we talked about before, numbers and the count, those are not averages. It's a little confusing because an average is a number, but you can't really combine two averages.[00:04:00] This one might be an average of a hundred things and this one is an average of five things and there's no clear way that once it's divided out into a number, how to combine them back together.
[00:04:10] An average is a quotient, right? An average is a division. And what if we just kept the individual pieces separate? So we kept the sum, which is the numerator, and then the count as the denominator, and we kept them separate like that so that we could divide them when we need them as a number, but we're gonna call that pair of the sum and the count the average.
[00:04:45] So now we can combine. We can make a function that takes two of these averages, two of these pairs. It adds the sums and it adds the counts and makes a new average of those two. [00:05:00] And that's fair because we've maintained them separate. So this gives us a way to have this combining operation of combining two averages into a bigger average which has the closure property.
[00:05:16] Now the trouble is where do you start? How do you get your first average if you just don't have any numbers yet? Well, you can make an average of sum zero and count zero. Any number can be lifted into the domain of averages by taking that number as the sum and putting a one as the count.
[00:05:40] So every number can turn into an average very clearly. Now you're always in this domain of averages and it's always easy to combine them and nest the expressions like that. So that's one of the things the closure property lets you do.
[00:05:59] [00:06:00] There's another example, from Structure and Interpretation of Computer Programs where the authors show that you could make a cool api. You have combining operations for images. So you take two images and you put them like left to right next to each other, and that returns a new image, which represents the two next to each other.
[00:06:27] And so again, notice the closure property. It takes two images and returns an image. There's other operations that will take one image and return a new image that is the flipped version of that image. You can nest these operations and make a whole bunch of cool images. Look at it the other way. The standard way of doing graphics with pixels is you would have to draw an image , at a certain point in the pixel [00:07:00] grid. Now draw another image next to it. Now draw another image on top. And the only dimension you have for expanding that image is to just add more of these commands. I believe that this helps move an API to feel more like a DSL. You start to have this second dimension of nesting where you can be very express with what you say because of that extra dimension.
[00:07:31] All right, that's all I have to say about the closure property.
[00:07:36] My name is Eric Normand. This has been another episode of my podcast. Thanks for listening and as always, rock on!