Single Responsibility Principle for Functional Programming

How do functional programmers use the Single Responsibility Principle?

Transcript

Eric Normand: How do functional programmers think about the single responsibility principle? Hi, my name is Eric Normand. These are my thoughts on functional programming.

The single responsibility principle says that a thing should only have one responsibility. One way to check if something has more than one responsibility is to say everything it's responsible for. If you use the word "and" or "or" or "but," then maybe it's not a single responsibility. Maybe, you have multiple responsibilities in there.

That's a good rule of thumb. It requires you to be pretty astute because you have to be able to articulate the concerns in that sentence well. What do I mean by that? I could say, "This object, this class, is responsible for keeping track of the orders coming into the restaurant." It sounds like one sentence. I didn't use the word "and." It's fine.

What does "keeping track of" mean? Is there some context about this being a concurrent environment? These aren't even operational concerns. These are still functionality-wise.

When you start thinking about those, it becomes clear like, "Well, am I talking about durability? Does this have to be stored on disk? What about if the power goes out or the network goes down temporarily? Is there some networking concern that I have to take care of? Is there, like I said before, some concurrency involved? How many systems are giving me orders at the same time?"

It's a pretty big mess of things. Just being able to say it in one sentence, "Keep track of the orders as they come in," is not clear enough. I see this happen all the time in the object-oriented world, where they'll think, "This is a very clear, small responsibility," but actually, it's 100 responsibilities.

It's deal with the concurrency. You want them to come in in a certain order. You need a queue. You are dealing with persistence. You are presumably persisting to disk. You are dealing with the errors that you have on the network as it flakes out. All of these things are real concerns. They're responsibilities of the object.

What a language like Clojure does, is it will separate out all of those concerns into their own little objects. For instance, you have something like an atom in Clojure, which has a single responsibility, which is to maintain a transactional state, to make sure that no two threads are writing to it at the same time.

You get a consistent transition from one value to the next. That's all it does. This is one of those things that when you come from a different paradigm, from a different language with a different style of thinking, with a different idea of what a responsibility is, this seems so tiny and almost something you weren't even thinking about before.

That is how Clojure gets its power. It's from building these small tools that have such a small responsibility that they can be clearly reused, and understood, and composed together to find a real solution.

When people talk about single responsibility, they often will make that sentence. They're not thinking about all of the responsibilities that a thing might have. This is one of the problems with reuse in classical object-oriented programming, in Java style object-oriented programming.

Someone will say, "It's the same problem." Someone will say, "What is the responsibility of this class?" It'll be like, "To represent a person, right?" It's like, "Oh, I said it in one sentence without using 'and.' That must be a thing, right?" The class has 300 methods. It's managing all sorts of stuff. When you look into it, it turns out well. It can read and write itself to the database.

Person means a different thing to different departments. Person in the HR Department is an employee. Person in the Hiring Department is a candidate. Person in the Accounting Department is someone that you might have to pay. All of these responsibilities are put together into this one giant object. It's not going to be reusable very much.

Those methods are going to confuse the different departments because they are using the same words that they're used to but with different meanings. Different departments have different meanings for them. It's just going to be not reusable.

If you go smaller, though...and this was the secret of Smalltalk and why people thought that object-oriented programming would give you something reusable. If you go smaller to something like a tuple, this just keeps two things together. That's its responsibility.

It keeps two values, a pair. It could be a three tuple or something. Then it keeps three values. You have a tuple that keeps two values. That's its responsibility. That's it.

You have an atom. Just make sure that state changes are transactional. Boom! Awesome. You have something like a HashMap. This maintains a mapping from keys to values. It's just very small responsibilities that are reusable because they're so small and regular that you can rely on them.

That's how we see this single responsibility principle. There's a rule of thumb of writing a sentence. It's just not enough.

You need to think about the problem and cut it up more, down to a smaller, granular level, to where you feel like, "Yes, this thing is so small, I cannot get it wrong. It has no corner cases because it's so small. It's just it. There's no way that this is wrong. I can rely on this, for sure." That's when you know that you've got something.

The level of abstraction you're at....It all depends on the other tools you have available in your language. A tuple might be a high level if you're writing an assembly because you have to deal with memory management. It's just not right.

It's not the right place for that. It's not something you can just rely on. If you got all this other stuff, like garbage collection and private methods, you can rely on those things and make something solid.

I've been rambling on with basically a simple idea, which is, break stuff down more until you feel this is solid. Then you can put it back together.

My name is Eric Normand. Please subscribe. If you're watching this on YouTube, hit Subscribe. You can subscribe to the podcast. I also love to hear from my listeners. I'm @ericnormand on Twitter. Also, you can email me. I'm eric@lispcast.com.

I'll see you next time.