What does it mean for programs to be built using "whole values"?

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

Subscribe: RSSApple PodcastsGoogle PlayOvercast

John Hughes, FP researcher extraordinaire, says Whole Values is one of the principles of Functional Programming. But what does he mean? We explore this important concept.

Transcript

Eric Normand: One of John Hughes's principles of functional programming is to use whole values. What does that mean?

Hi, my name is Eric Normand. These are my thoughts on functional programming.

Whole values is a subtle idea. It's something that I think needs to be shored up and made more definite. He doesn't ever explain it in definite terms anywhere. He just suggests we use whole values and gives some interesting examples of it.

I would like to try to define it, but I don't know if I'm ready. I'm just going to explore the idea a little bit and see where it goes. The thing is I do believe that he's right that this is an important thing in functional programming, it really helps.

I do believe in it. I use it myself, and it really feels right, but that doesn't mean I could put what I'm doing into words. That's what I'm trying to do right now.

Let's imagine that we have some imperative algorithm, and this algorithm requires us to keep track of two different numbers. We make two variables, and we update each as they need to be updated.

For instance, if you wanted to calculate the average of sum of a list of numbers, you could initialize two variables to zero. One of them is called the sum and one of them is called the count. You iterate through the list. Every time you see a number, you add it to the sum, and store it back in sum.

https://twitter.com/ericnormand/status/1055836722163081216?ref_src=twsrc%5Etfw

You add one to the count and store it back in count. At the end you divide them out and you have figured out the answer. At each stage of the loop, each iteration through the loop you got these two numbers. You got the sum and the count, but they're separate. There's no indication in your program that they go together. They definitely do.

As you know, I've talked about calculating an average using the ratio, so that instead of storing the numbers separately, you put them into a tuple where you have a sum and a count in the tuple. That's an example of keeping the values together and making them whole. Separately they're not meaningful, but as a whole they are.

This lets you pass it around like an argument. You can have a much more coherent set of operations on them because you have both values all the time.

https://twitter.com/ericnormand/status/1061650181488365568

When I've been reading about Smalltalk — again I bring up Smalltalk for some reason — but I think a lot of this stuff was known by the Smalltalk group. The original group in Smalltalk, the whole purpose of objects is to bundle two values together that need to be in a relationship, so that you can transactionally modify them to maintain that relationship.

That's all we're talking about is bundling values together into a composite value that those two values are kept in a relationship. It might be three, whatever.

Let's come up with another example. I was doing some coding yesterday. It was a recursive descent parser. One of the things I needed was two values coming out of a...let's say I parsed a number out of this stream of characters. I needed two things, the number that I parsed and the rest of the stream like where did I get to when I finished parsing the numbers. I bundled those up as a tuple.

If you extrapolate that to the whole architecture of the entire parser, not just parsing integers but parsing everything, you start to see that this bundling is very useful to be able to say, "Here's the value that I've got and here's the rest of the stream at all times."

Then you could be adding to that stream, I mean sorry, adding to that value. Let's say, you're parsing a list, you have to parse one item at a time. Add it to the list. You're maintaining the entire state of the parser in one value, one single value.

Instead of ad hoc, passing these things around as arguments and sometimes you need one and sometimes you need the other, you just always bundle them together. It just makes for a much cleaner interface to everything. I would like to give one more example. This is the example that John Hughes gave.

He was talking about a system that someone came up with to draw graphics. This was quite an old example. The example was that it was a system where an image was represented as a function. The arguments to that function where the axes on which is supposed to draw that picture.

You skew it to fit in the axis. You could have it whether orthogonal, the two axes and wherever the point is where they meet. That's the bottom left corner. Then, if you were to...sorry, you're backwards to what I'm seeing. Is you were to skew them like this or move it, it will move the image to another spot and will squish it.

That's interesting, but really what it's saying is that that function was complete as a whole value. It could be drawn anywhere on the screen, contained everything it needed to draw. The thing that was variable, where the arguments like, where it's going to be drawn or what the axes look like.

It was a complete unit. It did nothing. It could be completely opaque because its interface was very clear. It could be composed in very known ways. That's the example that he gave. I think that's another interesting example.

This idea of the whole value. Instead of having an image that was a ray of pixels, and then an algorithm, like a function or something else. I guess a function that would take the image and the two axes, make a new image that would skew it. Then take this skewed image. You could then draw that to the screen.

It was done in a much more elegant way that allowed for interesting patterns because you could pass in the axes to a function and it would change the axes, pass them to the image, made it recursively do it so you had this fractal effect.

It's much more interesting to do cool stuff with. It's much easier to do that functional recursive stuff when you're dealing with it at that level.

I don't know about how beautiful these images were by the way. I do find that that kind of thing helps where you have the whole value. I don't know about any other examples, but I'm going to try to define it now.

The real essence of it is that you take two or more values, usually it's a small number of things, and you put them into a composite value. That could be a tuple or you could define a new type.

https://twitter.com/ericnormand/status/1059113480102522880

You put them into this composite value and the composite value represents a semantic whole that you can then define new operations on top of. This is very much like...When I say it like this, it just sounds so much like object-oriented programming where you are grouping state together and putting an interface on it.

That's not what I see most people doing with object-oriented programming. Usually what they're doing is...They're not thinking in terms of the relationships between the data and how to make operations to maintain those relationships.

They're thinking more like, "What's all the data we can put in here? Let's group it together and call it a person or let's group it together and call it an inventory manager." I don't know what they would call it.

Then one of those operations that we need to do on it. We need to read it. We need to write it. We need...They're not doing this much more relationship based analysis.

Here's the tip. If you're doing object-oriented programming, you should look into doing it like this where you're thinking about...Instead of doing this for-loop and maintaining two different variables, those two variables should be bundled together.

Does it make sense to bundle them together? Once you've bundled them together, it could be that the operations...This make it functionally again. The operations on them have interesting properties.

If they're associative, that means you can do much more free recursion on them. If they are commutative, it means you can distribute your algorithm, not worry about what order things come back in. You're going to want to bundle those values together anyway.

Maybe if it's associative, you can find an identity value, you've got a monoid. There's things that you can do with them once they're bundled together. You can start to analyze the properties of those operations that maintain the relationship.

It's actually another principle he talks about. He talks about combining forms, it's what he calls them. Call them operations, but he's talking about you take two of these whole values and you combine them in some way.

If it's an average, you're taking the two averages, you add them up, you have another average. Take two of these images that take the same skewed axes and you can overlay them or you can put them side-by-side or something like that.

It's a combining form. I'm about to get on the greenway here, a lot of bikes. It's like a highway for bikes. I'm trying to define this. Trying to define this, this idea of whole values.

Before I define it, I did want to talk a little bit about this talk I saw by Fred George. Fred George is a...I guess he's a product manager now or project manager, something like that. He's an old timer, very experienced programmer.

I started getting into him because he talked about what his management style, which is called Developer Anarchy, or something like that. The idea is just give the programmers some KPI, some goals to hit and let them hit it.

Let them go and figure out ways to increase those metrics and don't give them user stories and stuff like that. Let them come up with them on their own. It required a whole architecture, like a microservices architecture so that they could work independently without breaking the entire system.

He also had another talk called The Secrets of Agile Programming or something like that. Secret Practices of Agile Programming. It was trying to say that when a lot of the agile practices were invented, a lot of them came from extreme programming.

It was assuming that you were programming, basically, like can't back. Do all these practices, do pair programming. Do continuous deployment. Do test-driven development. All these things that we think of as agile style things modern software development practices.

They also assume that your programming style, your coding style, was in a certain way, so we've described the few of these things. One of them, one of the things they said, was your classes should have two or at most three fields.

The audience was like, "What? That's not possible." And he's like, "Go, analyze your classes and the fields that they have. I'll bet that you'll see that some of those fields have a stronger relationship to each other than they do to the others."

If you have a person class, you'll see that, "Hey, this street name goes together with the zip code much more than it goes together with their salary. Maybe, those should start to be group together to represent that relationship."

That the whole point of this is to start representing the semantic information in ways that your programming language supports. If you're just throwing them together like a big bag, you're not going to be able to move this fast to program as quickly. You just going to have this big bag objects that just do everything.

The secret is to break them out into smaller objects that have much more coherent between the fields. I think that's very much related to this functional programming idea of whole values. Don't have a bunch of values that have a relationship and that they're split apart.

Like they're split into even something as simple as the two arguments to a function. Put them together. You'll notice that composite might have certain properties.

There's also the idea in a whole value that you could have partial solutions to problems. Instead of saying, "Well, we either have the solution or we don't," you might have a partial solution.

https://twitter.com/ericnormand/status/1024731587672526850?ref_src=twsrc%5Etfw

It's like this parser example that I gave where you have some value, and then the rest of the problem is being stored or is part of the answer. I parsed four characters for you, here's the number that that represents, and here's the rest of the stream.

This is all the characters that were part of that number. You put them together, and it makes sense together.

I'm going long now. I just want to say thanks for listening. It really helps me to get my ideas out, walking and talking, and sharing them with people. I love it that you...