Don’t call methods

Send and receive messages instead

Andrew Gibson
2 min readAug 21, 2023

Recap

In the previous article we explored the idea of passing structured messages to objects:

const nameMessage = { type: Name };
const spot = Dog(“Spot”);
spot(nameMessage)

And we simplified things using functions (with closures) instead of classes:

function Dog(name) { 

const base = Animal(name);
return message => message?.type === Speak
? “woof”
: base(message);

}

Returning messages

Now, we go one step further. Instead of returning raw values from our object, return a message instead.

import { Cart } from "./Cart.js";
import { AddedToCart, AtCheckout, Message } from "./taxonomy.js";
import { skippingRope, yoyo } from "./catalog.js";

const cart = Cart();

// 2 yoyos
cart(Message(AddedToCart, { ...yoyo, quantity: 2 }));

// 1 skpping rope
cart(Message(AddedToCart, { ...skippingRope, quantity: 1 }));

// checkout
const cartTotals = cart(Message(AtCheckout));
console.log(cartTotals.items, "items, total cost:", cartTotals.total);

cartTotals refers to a message of type CartTotals although we’re not inspecting the message type.

In fact, the code looks more awkward than it would be using normal class-based code. But, that’s because we’re at the awkward half-way point in our transition.

Changing paradigms

When you switch paradigms, you often reach this awkward half-way point. You are bridging two worlds. Here we are expecting a `return` message. The real magic happens when we no longer distinguish between the sent and the received.

But, for that we need a few more pieces of the puzzle which will come next time.

Taxonomy

Normal communication relies on a taxonomy of sorts. The _Type_ of a message (not the `object` class or method) is the ultimate signifier.

In today’s code sample, we import the taxonomy from a module which looks like this:

export const Type = Symbol("Message type");

export const AddedToCart = Symbol("Added to cart");
export const AtCheckout = Symbol("Checkout");
export const CartTotals = Symbol("Cart totals");

export const Message = (type, props) => ({

[Type]: type,
...props

});

Time

Notice that the messages above refer to events in time. Over the last twenty years or so, advances in modelling leverage time. Think of event-based programming, DDD, event modelling et al. Time is the common distinguishing factor.

When implementing OOP, I use messages which state an event in time. This leads to a clean model because it’s how human beings think.

What is absent is the idea of “command”. I find this isn’t necessary. Commands allow for a multitude of outcomes. If instead, objects state what has happened, it is unambiguous.

Conclusion

We’re at the tipping point toward a message-based OOP implementation. Next we’ll look at how to get rid of the necessity of “request-response” / “call-return”.

https://github.com/goofballLogic/modd/tree/article-2023/articles/002-CallAndResponse

--

--

Andrew Gibson

Business and technology in the software engineering space