Ah these are all very interesting points! I’ll try to study them a bit more, thank you everyone for all the context!
simplicity over DRY
if you accept just a little bit of code duplication
Sounds good! This part brings to mind how loosely coupled designs (over DRY) incurs a bit more code duplication (forwarding calls, repeating models but keeping them separated etc.) in exchange for greatly added benefits. And this seems to be well documented on internet with the usual languages having adhoc polymorphism to do dependency injection. When trying to write code in ML languages (like Rescript/OCaml), I often have trouble seeing how to do the same thing: modeling the world loosely and enforcing contracts (OOP composition + interfaces vs a more data+functional way in Rescript?)
For example, in these other languages we could do composition over inheritance and define data types then define multiple interfaces/traits (only for behaviors) and make these models honor these contracts (even multiple interfaces). In that way, it could be still be WET (instead of DRY) but we would have clear contracts that are enforced. Golang is the epitome of this where we can inherit pure data (struct embeddings) but only compose behaviors (with interfaces and composition through struct embeddings)
I’m having a hard time reproducing these situations when iterating on Rescript. Sometimes a functor or higher module seems to be the way but it indeed feels overkill. For example, is there way to make a module Warrior honor 2 “interfaces” or is there another way (functional) to think about it? This is all in the context of modeling things on the backend (having clean code decoupled layers, creating repositories, D.I etc.) or to have fun modeling entities in a small game