Hacker News new | ask | show | jobs
by bob1029 700 days ago
The objects are just one tool. You usually need a few to do the job well.

The biggest thing I've seen blow up actual OOP projects has been a lack of respect for circular dependencies in the underlying domain. If you have one of those problems where it is ambiguous which type "owns" another type, then the moment you start writing methods in these types you are treading into the dark forest. Often times it is unclear that your problem exhibits circular dependencies until code is already being written and shipped.

My approach to these situations is to start with a relational data model. A SQL schema (and its representative DTOs) can model circular dependencies competently. You can then have additional object models (views) that can be populated by the same relational data store (just a different query). One other advantage with the relational modeling approach is that it is very easy to explain things to the business before you write a single line of code [0]. The purpose of a SQL table can be demonstrated with a sample excel sheet with mock business data.

This path was largely inspired by Out of the Tar Pit [1] and practical experience in fairly wicked domains (semiconductor mfg., banking, etc). I am not sure Functional Relational Programming is the answer for everything, but the "Relational" part certainly seems to be universally applicable.

[0]: https://en.wikiquote.org/wiki/Fred_Brooks#:~:text=Show%20me%....

[1]: https://curtclifton.net/papers/MoseleyMarks06a.pdf

1 comments

Why would a type ever own another type? What would that even mean? Like an inner class? Or are you taking about ORMs?
Think about a collection of one related type on another, such as Customer -> Accounts + Account -> Customers. In banking, it is possible for one customer to have many accounts, and for each account to have many customers.

OOP will let you express this kind of problem from a data modeling perspective (List<T> on each type), but from a serialization and dependency perspective you have to pick a "winner". In banking, it is unclear which type should be king.

The relational approach is to use a join table. Model the actual relationship itself and its relevant attributes (role on the account, beneficiary %, etc). This also handles any arbitrary graph of accounts and customers, assuming you are using a modern database engine that supports with recursive & cycle keywords.

ORMs will blow up on this kind of thing without special handling.

> OOP will let you express this kind of problem from a data modeling perspective (List<T> on each type), but from a serialization and dependency perspective you have to pick a "winner". In banking, it is unclear which type should be king.

Serialisation is not necessarily a big deal, just pick one and refer back to it — check out PRINT-CIRCLE, Sharpsign Equal-Sign and Sharpsign Sharpsign in Common Lisp.

For dependencies, I think this only matters with strong type systems which don’t support forward declarations, or which lack null references/empty containers (if the language does support that, just create one object without any references, create the second, then add the second to the first).

This is related: Why relations are better than objects https://www.cell-lang.net/relations.html
> Why would a type ever own another type?

FHIR, an international standard for medical data, is a great example here. The circular types get so gnarly that I've personally managed to infinite-recursion the TypeScript compiler. We even managed to get 45min builds followed by a timeout by sneezing wrong.

https://www.hl7.org/fhir/overview.html

The tldr is that you can have things that belong to things that belong to things that end up belonging to the original thing after a very very long inscrutable chain of pointers. Essentially a graph.

Maybe like how having a handle back to something can be a parent-child relationship or can imply that the child owns the parent, especially if the lifetime is extended by the ownership (e.g. shared pointer).