Objects as Records vs Activities

Anything is better than nothing.

Of course, if you want to print in a specific printer, you need to model that too. There are many reasons to do that, depending on the social norms, the property relations, the location of the printer etc…

It is up to you to decide which model to define and how granular your notion of equivalence is.

1 Like

Mmm. That all seems like quite a lot of stuff to have to model, doesn’t it? Particularly the “social norms and property relations” part. Seems like we risk entangling a whole lot of unrelated concerns together when we make our modelling that rich.

I think my deep sense of ambiguity about objects/actors comes down to this issue of modeling. And whether it’s better in general to have “thin” models or “thick” models. I find myself very split on this issue. I feel like I’m not alone in feeling this split.

For example, here’s one recent take, pushing for very thick models entangling lots of domain concerns, in the classic 1990s “Object Oriented Analysis and Design” way: Jeremy Carter’s “Thinking In Actors”. Thinking in Actors - Part 1 - by Jeremy Carter

Modelling your domain is more than creating data structures, it’s about capturing business processes, behaviours and interactions. The end goal is a model that is rich enough that it truly reflects the real-world.

When you have an anemic data model we see objects in our system that are just data containers - without meaningful behaviour. A more domain driven approach would have us encapsulate business logic, state and behaviour inside the domain object itself.

Yes, that’s definitely that expansive all consuming '90s OOP flavour I remember. Model all the things! Especially, model the human behaviours and interactions! Modelling just data is “anemic”! And this seems to be what Alan Kay keeps pushing for. Much more rich, complex, domain-driven objects, which… can’t ever be repurposed for any other domain, ever, because they’re so absolutely hard-wired to that one specific company or business process.

This makes a lot of sense, I guess, if you are in fact a company, and you are building your very own business process, and you have 100% control over all the objects you build, and the platform you’re writing your objects in will last forever, and nobody outside your company is ever going to interact with those objects or try to reuse them in any way except as part of your predefined business process.

But as a user - caught in the unmapped spaces between companies and their often abusive and always neglectful business processes - having all your computing objects come pre-wired into someone else’s business process doesn’t feel great. We’d like portability away from companies, and away from their processes.

Then on the exact opposite of the thick/thin objects spectrum is Danny van Heumen’s “Minimal Objects” philosophy, which says that an object should do just as much as is necessary to keep all its data in a “valid, consistent state” - and absolutely no more.

https://dannyvanheumen.nl/post/engineering-simplicity-minimal-objects/

Minimal logic ensures that the methods do not dictate the way in which the class is “supposed to be” used. The only responsibility of the class is to itself: manage its state as defined by its invariants.

Minimal logic in methods leaves maximum room for reuse of logic in utilities, design patterns, etc. (Or in other, better suited locations.)

And if our objects were immutable, then “minimal objects” would presumably collapse down to just constructors, accessors, and not much else.

I can see both philosophies as having merit, but it’s a little jarring that it’s the mid 2020s and still two utterly opposite philosophies can both believe themselves to be the true spirit of OOP.

And I feel that ambiguity too. I want a philosophy that’s clearer, for small personal knowledge/automation systems (that maybe have both data and behaviour, and can be easily distributed, and need to have a runtime that’s simple enough that there could be multiple implementations)… but I feel the attraction to both poles of this argument. If you do have the luxury of hand-writing your own automation, for your own purposes, then it is very tempting to model your own domain concerns straight into executable code.

And maybe that’s even the right thing to do? Like, the dream of objects is building your own state machine, with your own custom nouns and verbs, that gives you precisely the domain model you want. Maybe it’s the same dream as Forth, of evolving the language into your own DSL. Except Forth doesn’t really have nouns, just verbs. And both Java and Javascript are pretty terrible grammars for DSLs. But another, better, OOP language might crack it.

But… I look back at the 1990s and all I see is a highway full of the wreckage of crashed big domain-driven-OOP designs.

2 Likes

You should have both poles at the same time.

In essence an object should be considered a dynamical system. The order of the invocation of the methods or the context in which they are invoked should be irrelevant.

A dynamical system has certain invariants.

Now business logic constructs also a dynamical system. With its own invariants.

The class hierarchy system of smalltalk does not préserve invariants, thus it is a bad model of abstractions.

Consider a class which is a circle that is extended with a z variable into two subclasses, a cylinder and a helix.

Any method that moves to a point in the cylinder or the helix is a refinement of a method that moves to a point in the circle. Their dynamical systems are a refinement of the dyn system of the circle.

But most subclasses don’t do that. Their methods are arbitrary.

(This is TLA 's concept of refinement. It does not use a type system, that is the reason I am using it as an example when I am talking about smalltalk.)

On another note, I do not know all the particularities of each system, so I can’t comment on everything that existed in the last 60 years :slight_smile: . For example, even in Agda , it took my many years to understand that there are many problems with the way we program in it.

3 Likes

I agree that an object is a dynamic system, and that if we’re going to have dynamic systems rather than static/immutable records in our software, we should try to at least make sure that they can only interact in clearly defined ways. Almost all security vulnerabilities in current software are because of software objects that are accidentally interacting in ways not specified in their interface (as understood by the user or programmer), because the operating system or language runtime did not in fact enforce that interface and allowed it and its security expectations to be bypassed. With ongoing tragic results.

Also I agree that we should definitely have a way of specifying the invariants in dynamic systems, which is something that I think the currently-popular concept of “interface” doesn’t do. Ie that it’s not enough just to specify the type signature of methods, but we would need to specify also what kind of output we get and how it changes (or doesn’t change) the output of future methods.

And maybe such a way of specifying invariants is all I would need to resolve my lingering uneasiness about object systems as opposed to pure-function-over-typed-immutable-value systems.

(I still have plenty of uneasiness about type systems, especially about how many there are, how complex they all are and how nearly-impossible it is to get multiple type systems to interact in one programming language or runtime. Which is something I feel we would have to solve on Day One before we could even begin to get a universal typed-functional runtime, yet there’s so little work being done on this. It’s hard to even describe the problem to someone deep in type theory, which always means their favourite type theory existing alone in a custom-built runtime and compiler of their own design – and not what we actually need to consider, which is the set of all current and future type theories, interacting with each other in one environment and constantly receiving updates to the definitions of not just types but also type theories, in one never-rebooted runtime.)

So invariants, yes. Those would help tame a lot of the wildness of dynamic systems.

I feel like finding a language in which to both express general invariants of systems, and to express and construct systems in terms of those invariants is the key. And preferably that would be one single language, not two unrelated ones bolted together, as we currently have with functional languages plus typesystems. I also feel that any language meeting this specification is likely to look like a general logic solver of some kind, in which hopefully various type or logic or proof systems (like TLA+ and many others) can be executed as libraries rather than hardcoded in.

I’m a little confused though how we could ever have a dynamic system where “order of the invocation of the methods is irrelevant”. The context that methods are called from, sure, we can ignore that. And yes we can lock down the behaviour of a system with invariants. But isn’t the whole point of a dynamic system that, invariants or not, its state does evolve over time? And therefore, the results of methods invoked on it (or abstract properties of it) are also definitely going to evolve over time too? So the order of invoking methods is definitely going to matter very much? And if the order of method calls on an object ever doesn’t matter, then that could only be because it’s a static system, not a dynamic one?

Eg if I have an object that’s modelling the Logo “Turtle”, it’s surely part of the definition of the Turtle dynamic system that it’s always going to matter very much in which order I send it the methods “forward 50 steps” vs “left 45 degrees”?

1 Like

I would recommend anyone to view the video lectures of Lamport, even if they never use tla+. It explains everything with amazing clarity, (and they are very amusing and fun, for some reason.)

https://lamport.azurewebsites.net/video/videos.html

Recently, I saw this paper, where they managed to compile tla+ specifications.


On your turtle example, the point is that you need to be able to move with any of the methods, in any order.

There is no correct order, that if you don’t do it, it will crash or the invariant will not hold. For mutable variables, that happens a lot.

2 Likes