What is an inverse, and why is it useful?

This is an episode of Thoughts on Functional Programming, a podcast by Eric Normand.

Subscribe: RSSApple PodcastsGoogle PlayOvercast

Inverses are everywhere. They let us undo an action. For instance, I can open a door and close it. Why do we want to do this? Because there are things I can do with the door open, and other things I can do with it closed. We need this same flexibility in our computer programs.

Transcript

Eric Normand: What is an inverse? How is it useful? By the end of this episode, we'll go over a few uses for inverse, what it means, and why we might want to make something not have an inverse. My name is Eric Normand. I help people thrive with functional programming.

Inversion is a very useful algebraic property. The way I like to think about it is, from the real world, we often do things that we can then undo. Imagine if you could open a door, but you could never close it. You could climb up a mountain, but then never come back down.

The inverse captures this idea of reversibility. Basically, it's a property of usually two functions. The two functions are inverses of each other. One can undo the other, and it usually works both ways. A simple example is, if I add one, so I increment a number, the inverse of that is decrement the number. It goes back to the value before.

More formally, I would say, g(f(a))=a, g and f are inverse of each other. You could also put it the other way, f(g(a))=a. If I apply f and then I apply g, I get back to where I started with a. This is more common than you think.

The main use is that you can move something into a state space. Let's say, a new type, a new structure to the thing, where the problem you're trying to solve is easier or even possible. It wasn't possible in the other space.

For instance, if I have some data structure, some nested thing, I can serialize it to JSON. That's my operation. Now as a JSON string, that I can send over the wire. It goes from this in-memory representation with pointers, and whatever else your language needs to hold that structure. It goes into a JSON string, which is just a linear sequence of characters.

Now, I can send it over the wire. That's something I couldn't do before. On the other side, I can do the inverse of serializing it, deserializing it, parsing it, and turn it into a data structure on the other side. I have serialize and then deserialize are inverses of each other.

That's a common example of something that is an inverse and a practical application. You can't send the data structure itself over a wire. If you choose a linear character format for it, some kind of byte format for it, you can.

Then, on the other side, in the byte format, there's things you can't do to it either. You can't act on it recursively. You have to deserialize it out of that byte format back into some in-memory representation of it.

Some other things that I like to do, you might think of as boxing and unboxing. If you have a native number format in your language, you could box it up, and now it's treated like an object. It has methods and stuff.

Then, when you need to actually add it to something internally, your language is going to unbox it, add it, and then probably rebox it up. Boxing unboxing, another inverse.

There's an interesting thing that some functions are their own inverse. Negating a number is its own inverse. If I have one and I negate it, now I have negative one. I negate it again. I get back to one. It's its own inverse. It's pretty cool.

This is an easy property to test because the formula for it, the definition of that property, is so simple. You can do a lot of property-based testing on it.

This is one that's given a lot. One is an example that's given a lot when people talk about property-based testing, this idea of testing a round trip. You can take a value, run a function on it, take that output value, run another function. It should be equal to the first thing you had. It's very common property.

There's also, sometimes, the property is that there exists no inverse of a function. This is something like a cryptographic hash. If I have some sequence, some data, and I run a cryptographic hash on it, I have lost the ability now to get that data back out.

The hash is lossy. I cannot get back to where I was. That is the desired property of a cryptographic hash because that's what you're looking for. You're looking for some secure identity of this data that does not actually contain the data itself.

I'll recap. I guess this is going to be a pretty short one because it's a pretty clear property. We have a notion of it already. This is an algebraic property, usually, of two. Like I said, sometimes, you can have one function be its own inverse. Typically, it's two functions that undo each other.

Usually, you can call them in either order. I can start with a value, call the first function on it, then call the next function on it. I get back to where I was with the original value. Some functions are their own inverse. Sometimes, you want a function to not have an inverse. It is proven or somehow tested pretty well that there is no inverse.

If you liked this episode and you want more, you can go to lispcast.com/podcast. You'll see all the old episodes. Every episode has video, audio, and a text transcript. You'll also find links to subscribe and how to reach me on social media.

I love getting into discussions. I love talking about this stuff. If you're so inclined, if you have something to ask, something to tell me, if I made a mistake, please let me know.

Thank you so much. Rock on.