- Published on
The ambiguity of objects
- Authors
- Name
- Damian Płaza
- @raimeyuu
Wanna eat a burger?
Imagine there's a guy named Chris, who took his fate in his own hands and started his life career as CEO of a little burger foodtruck.
"Anti-hunger", he called it.
Chrishad the experience and the idea how to run the best burger foodtruck in town.
Even though people adviced him to start a restaurant, hire chefs and staff, buy very expensive cooking equipment, he decided to start small.
"But what if you suddenly need to scale infinitely?!", they asked.
So Chrishad his little foodtruck.
This is how it all began.
Anti-hunger process
Everything started with a customer having a desire to battle the hunger.
Chriswas ready to take the order and write it down so that he knew what to prepare.
Then Chrisneeded to prepare the beef.
He needed to take a raw beef and flip it two times on the grill, with a strictly measured time and temperature, to get the perfectly grilled beef.
Next phase was to prepare the bun.
It was pretty simple - just slicing it.
When the perfectly prepared beef and sliced bun were ready, Chrisneeded to put them together.
He added his homemade cheese and homegrown tomatoes - composing everything together, in his special way, created the best experience for the customer!
Chrisalso decided that each of his burgers should be packaged in a dedicated box, with a special message on it.
Just to make the customer feel special.
When the burger was ready - well-prepared, well-composed and well-packaged - Chrishanded it over to the customer.
So this event turned a hungry customer into a happy customer.
That's everything that Chrisneeded to do to make his customer happy.
That was the "Anti-hunger" process, in the nutshell.
Chrisknew every single step, all the rules and all the details of the "Anti-hunger" process.
He was able to transform a raw beef into a perfectly grilled beef, based on the set of instructions he had in his head.
Same for turning a whole bun into a sliced one, and finally composing everything into a masterpiece.
He was responsibile for everything and because of working alone, he didn't need to share any information with anyone.
Chriswas a true virtuoso - he was able to transform raw products into a magical experience!
What?! More customers?!
Chriswas definitely doing great.
People were recommending his burgers to their friends, and they were coming to his foodtruck to try them.
In a blazingly fast time, Chrishad a line of customers waiting to order his burgers.
That made him both happy and concerned.
He knew he wouldn't be able to handle every single step alone.
That was his eventual decision - "I need to delegate some of the responsibilities!".
So he hired a new team members: Ben, Jim, Mikeand Henry.
Chrisneeded to organize the work in a totally different manner, as before he didn't need to collaborate.
He was responsible for the entire processing.
Chrisdecided that he is going to be responsible for taking the orders and for having a lovely chat with the customer.
He is going to play a role of a "frontman".
But from now on, everything was about to change.
Anti-hunger process, version 2.0
The role that Chrisplayed was similar to the first step from the initial version of the "Anti-hunger" process.
Placed order was very important fact for Ben- he was responsible for preparing the beef.
Benwas a master of grilling beef - he took all necessary training from Chris, becoming the expert in the domain of burger grilling, and eventually understood the secret of "Anti-hunger burger".
There was one difference between jobs of Chrisand Ben- along with yielding the perfectly grilled beef, Benneeded to send a message regarding his part being finished.
Then, Jimknew what to do and he was able to pick the burger up to perfom his part - taking a bun and slicing it.
When Jimfinished his slicing part, he needed to send a message that the bun became sliced, providing the product of his processing too.
The big final! Composing everything together, with some additions - that was the job of Mike.
Because everything was working in a perfect, sequential harmony, Mikewas waiting to respond to a "call" from Jimabout bun becoming sliced so that he could grab both perfectly grilled burger and sliced bun and start his part!
Of course, as before, Mikeneeded to send a message that his part was finished.
Henrywas sitting there and, waiting to pick up the message and get the Anti-hunger burger and package it.
Next to the ready Anti-hunger package, Henryloudly notified a hungry customer that his hunger is about to be defeated.
The customer was able to respond to a call and pick up his Anti-hunger burger which resulted in a happy customer.
When the customer experienced the joy of battling the hunger, he was ready to share this experience with others, by sending text messages to his friends.
Chris, Ben, Jim, Mikeand Henrywere collaborating together to provide the best experience for the customer - each of them was responsible for a different part of the "Anti-hunger" process.
"Anti-hunger" process, version 2.0, was a success!
They were working in prosperity, with a clear understanding of their roles and responsibilities, evolving into the best burger foodtruck enteprise in the whole town!
You must be kidding, right?
"What are all those fancy pictures, Damian?!", you might ask, dear Reader.
You might feel that I lured you to read about objects, explain intentions with code, but instead I gave you a tale about a guy who is making burgers.
Fair point!
Let me start from the beginning then.
From time to time I observe the eternal fight between two groups of people - those who are in "favor of objects" and those who are in "cursing on objects" (simply saying).
Objects might seem an obsolete tool for solving modern problems.
"We all shall use functional programming!", some people say.
"I need no objects - I just need data and transformations!", others comment.
Well, it might be all true but the problem is that there is too much focus on "objects" and not of an phenomenon that emerges when they start collaborating.
Object-ism
As the famous Zen Koan says:
“When a wise man points at the moon with his finger, the fools look at the finger, not at the moon.”
We could have similar when it comes to objects:
“When a wise man points at collaboration via message passing with objects, the fools look at objects, not at collaboration via message passing.”
I am not a wise man, that's for sure, but a smart guy, named Alan Kay, once said (paraphrasing):
OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.
When we think about messaging for a while - why are we sending messages? Why are we exchanging thoughts? Why are we communicating our struggles?
To interact.
To collaborate.
To organize ourselves.
To solve problems, together.
When Chriswas working alone, he didn't need to communicate with anyone.
We was able to transform raw products into a magical experience.
During applying those transformations, he used his mental models - even though it was highly unconscious process (pun intended), still it all translated to lower-level instructions to follow.
When Chrisinvited collaborators to join the crew, transformations did not disappear, they got assigned to different roles.
Of course, he needed to share specific rules, related to each transformation, by teaching his fellow team members.
Objects, transformations and responsibilities
Let's take a look at grilling a beef.
One could say is forms a function:
type GrillBeef = RawBeef -> OneSideGrilledBeef -> GrilledBurger
That signatures enforces some contraints, e.g. you cannot turn a raw beef directly into a grilled burger.
In other words, to get a GrilledBurger
, one needs to pass a RawBeef
, then pass a OneSideGrilledBeef
.
Such simple transformation can be done by one person, right?
We saw it during our little tale - initially Chriswas able to do it all by himself but eventually he assigned this responsibilitity to someone else.
And by the way, what is a responsibilitity? (ah, my sweet definitions!)
Possibly the simplest one is:
An ability to respond to a call.
So someone, who was assigned to this role, should be able to respond when someone else calls him to get the beef grilled.
Bencould sit there all day long and do nothing until get got called to perform his role.
He then took the raw beef and transformed it into grilled burger.
Of course, he needed to use some external tools like oven, frying pan, etc. but that's a different tale, dear Reader.
The main point is that he was doing some kind of processing, when he was called to do so.
That's why he started his job only when he received a message from Chris.
Same goes for completing turning a raw beef into a grilled burger - he send a new message.
Transformations and message passing are two sides of the same coin.
Without message passing, transformations are useless (no one uses them).
Without transformations, message passing is vain effort (nothing is produced).
Transforming Core, Messaging Shell
Imagine that someone had a crazy idea to split grilling beef into two parts:
- one part is responsible for flipping the beef
- the other part is responsible for second flipping
Let's assume there are some rationale behind that.
As we saw in our little tale, each of the collaborators had his own burger preparation station.
Let's say it's same for this idea.
What it means is that each of those functions need to be coordinated to get the final job done - getting a grilled burger.
The core transformations could be represented by functions:
type GrillOneSideOfBeef = RawBeef -> OneSideGrilledBeef
type GrillSecondSideOfBeef = OneSideGrilledBeef -> GrilledBurger
Without collaborating roles, the partial product - grilled burger - cannot be achieved.
They would need to communicate with each other, exchange information about their job and information about results of their processing (not processing results but information about them).
Those two parties would need to excel their core functions on a call, so core transformations could be "packaged" into:
type GrillOneSideOfBeef =
Message -> RawBeef -> (OneSideGrilledBeef, Message)
type GrillSecondSideOfBeef =
Message -> OneSideGrilledBeef -> (GrilledBurger, Message)
If one looks closely, it starts with a message and ends with an another message.
Inside there's a processing, a transformation, that takes values and produces a new value/new values.
How does it relate to "object-orienation" that all of us hear here and there?
Message-passing on the outside, functional in the inside
When I think about canonical resource for "object-oriented design", I think about one of the best books on that topic I know - GOOS.
In the section "Different Levels of Language", the authors wrote:
As well as distinguishing between value and object types, we find that we tend towards different programming styles at different levels in the code. Loosely speaking, we use the message-passing style we’ve just described between objects, but we tend to use a more functional style within an object, building up behavior from methods and values that have no side effects.
"What a discovery", one might say.
That has profound effects on one's thinking, though.
Even though your language might have a strong concept of a "class" but mentally you should think in two categories: transformations (functions that process values) and messaging (collaborating parties).
The ambiguity of objects gives as false impression that they are the most important part of the paradigm, whereas they are just value-transforming, collaborating messengers.
As Sandi Metz once stated:
You don't send messages because you have objects, you have objects because you send messages.
At least from the outside, that's true - as this is something we shall only see, as they should take care of their "job security", by hiding the way they do their job.
But from inside, they are masters of processing - where values, transformations and becoming level (if you know what I am talking about, please check Modeling Maturity Levels) are really important.
From objects to actors
If it's about message passing, communication and collaboration - what is then an object?
Is it an instance of a class that is able to respond to a call?
Is it a web service that is able to respond to a request?
Is it an event handler that is able to respond to an event?
That's what comes with focusing on messaging - one is able to leverage collaboration between parties, and not focus only on "collaborators in the small scale" (e.g. objects in C# or Java).
And turns out there's a programming model that takes messaging quite seriously - it is your beloved Actor Model.
Somehow when we think of actors - messaging and collaboration is a natural way of reasoning - but when we think of objects - we tend to focus on structures, properties and inheritance (yes, I know I might be exaggerating here).
When a language, that one is thinking in, has a strong concept of a class
, "an obejct" mistakenly can be understood as something with "is-a", "has-a" attributes (which strongly manifests "being" level - you can find more in Modeling Maturity Levels, dear Reader).
Actors are natural collaborators, social creatures by design - somehow we forgot same aspects when it comes to objects. (and of course - I totally neglected one of the biggest advantages of actors, namely their superpowers when it comes to concurrency - but this will come, some time later)
I don't remember when I read it, but it's a quite nice summary of such sad dichotomy:
Every obejct wants to be an actor but eventually becomes a table.
And what about functional programming?
It's all about transformations, right?
Even though it's all about functions, transforming values into another - at some level, one will encounter the need to delegate the further processing to others - whether they are web services, serveless functions or database stored procedures.
Transformations and message passing are two sides of the same coin.
Same goes for collaborating parties - when one looks from a specific perspective, it all starts forming a processing function, e.g. that is able to transform hunger into magical and satisfying experience.
And still we might be able to look inside and find a stream of messages, working as facts, happening throughout the large-scale transformation:
Pass values, invite collaborators
I firmly believe that the language we use shapes the way we think (thus see the world around) - in small scale and in large scale.
This tiny tale I brought to you, dear Reader, is a metaphor - and as metaphors have in their nature - they are incomplete (if they were complete, we would be talking about a reality or something close to a reality).
I am conscious it does not cover all the aspects, it has holes/gaps and is created with general simplification - but it is just a model.
A model of thinking.
Treat your objects as units of behavior, capable of processing and transforming values (often represtend by objects!) - and you understand only half of the truth.
Treat your objects as collaborating messengers, capable of exchanging information (often represented by objects!) - and you understand only half of the truth.
Next time, dear Reader, consider various scenarios and levels - when you need to make two parties collaborate and when you need "just" transform something into something else.
Some time you might transform PendingDocument
into ApprovedDocument
, and sometimes it will be just turning RawBeef
into GrilledBurger
.
Then, the question might be - what roles need to collaborate to get the job done? (you might be interested in I, interface and in The ambiguity of interfaces for more about roles)
Objects, web services, event processors, actors - from a certain angle and level, they are all the same.