Callable objects are objects that you can call like a function, and which maintain some internal state. They have getters and setters for managing state, and when invoked like a function, the internal state of the object determines the behavior of the function.
They can be useful when you want to perform some operation multiple times where only one of several arguments to the operation is changing each time you call it. Because you have an object that stores it’s own state, you can set the state on the object and hold onto that reference, then you just call it like a function whenever you need.
Let’s take a simple greeter function as an example. The function combines a salutation and a subject to form the greeting.
If I were to implement this as a callable object, I’d probably make
the salutation part of the object state so that we could set it once
and then use the greeter to greet multiple subjects. To use it, we
would first create an instance of the greeter function, then we use
salutation accessor to set our salutation. From then
on, we can just call the greeter instance with the subject we want to
greet and it will use the salutation we set on it.
This can come in handy if we’re applying this function to multiple arguments.
And it’s especially handy if you want to pass the function off to some other function as an argument.
Every time you invoke
greeter(), you’ll get a new copy of
call() function. The
call() function is
where we’ll implement the actual greeting logic. We’ll want
call() to take a subject to greet as an argument, so for
starters we’ll implement it like this:
The next thing we need to do is add a property to the greeter for the
salutation to replace the hard-coded
We’ll add a variable to the closure to hold the state, and add a
property to our
call() function to get and set the
If you’re thinking that this looks an awful lot like currying a
function, you’re not wrong. The big difference is that instead of
passing in the state when we call
greeter(), we set the
state using accessors. This has advantages in some situations. It
allows us to change state later without having to call
greeter() again to create a new callable. It also allows
us to defer some of our configuration to a later time.
If you had a lot of state that makes for too many arguments passed to one function, you could go the class route. In this case, instead of invoking the object directly, you invoke a method on it. You know: object-orient programming, if that’s your thing.
It’s possible that callable objects are appealing to me because they’re so common in D3, and I really like working with D3. Why D3 makes so much sense to me, though, I can’t say.