This is a short post intended to describe a category theoretic formalization of the concept of stateful objects, as employed in object oriented programming and in some styles of functional programming, and as a general concept.
Here is my thesis. A stateful object is a concrete monoid.
What is a concrete monoid? There are many ways to describe them, and I will choose a simple description which lacks dependencies on other concepts from category theory.
Definition. A concrete monoid M = (S, T) consists of the following data:
- A set S of black box objects, which we can think of as possible states of the stateful object.
- A set T of functions f : S -> S mapping states to states. These are the elements of M when M is considered as a monoid. They are the arrows of M when M is considered as a one-element category. When we think of M as a stateful object, the elements of T are the possible actions that can be performed on M. Each maps any given possible state of M to a new state; this describes how each action affects the state of M.
In OO terms, the elements of T can be thought of as method calls, with each set of distinct arguments to a method yielding a distinct element of T. So for example if M has a method “foo” which takes an integer and a boolean, then foo(3, true), foo(5, false), foo(-3, true), and so forth are each distinct elements of T.
Suppose now that we have multiple stateful objects. Suppose they might interact. How can we model their interaction?
I think that it’s appropriate to apply different models depending on the nature of the interaction between some stateful objects, and to apply the simplest model that describes the case at hand.
Let’s consider a simple case. Let S = (SS, ST) be a stateful object representing a server (e.g. a Web server). Let C = (CS,CT) be a stateful object representing a client (e.g. a Web browser). Let’s idealize away for the moment the intermediate layers between S and C, pretending that S and C are two stateful objects that interact directly with each other, by rubbing up against each other as it were. Their only method of interaction is the prototypical client-server interaction where C makes a request to S, and S provides a response to C.
We can describe this interaction by the following data:
- A function req : CS -> Maybe Request. This function tells us what request the client will make in a given state, or it tells us that the client isn’t going to make a request in the current state.
- A function res : Request -> Pair ST CT. This function tells us what response the server will make to a given request. The response is represented by giving an element of ST which describes how the state of the server is affected by processing the request, and an element of CT which describes how the state of the client is affected by processing the response.
Given this data, a complete interaction between the client and server can be inferred from given initial states of the client and server. Let cs be an element of CS, representing the initial state of the client in the interaction we want to model. Let ss be an element of SS, representing the initial state of the server. req(cs) tells us what request, if any, the client will issue. If req(cs) = Nothing, then no request is issued and no interaction occurs.
Suppose on the other hand that req(cs) = r is some Request object. In this case the client issues the request r to the server, and the server processes it to receive a response. The server’s response is described by a pair (st,ct) = res(r). st : SS -> SS is an element of ST: i.e., a transformation on the server state. ct : CS -> CS is an element of CT: i.e., a transformation on the client state. As a result of this interaction, the client C advances to state ct(cs), and the server S advances to the state st(ss).
I leave further extrapolating these ideas as an exercise to the reader. Your feedback is welcomed!
I am not sure whether these ideas are new or not. Please comment if you know of prior articulations of this set of ideas or similar ones.