Haha. It’s funny you mention that. While that outline I started with is very simplified, I first learned this technique from a tutorial about the IO monad in Haskell (
yes, I used the “m” word). My core takeaway was to separate the pure logic that can be easily tested from the side-effects that had to be wrapped in a monad. It’s about separation of deciding what to do from actually doing it. I’ve found it a useful distinction.
Fascinating. I find LSP a “nice to have” and have never let it affect how I design my code. Perhaps that reveals too much about me though
Still, it is important to recognize it has tradeoffs, like all design decisions do. The case expression is a nice mitigation of your concern.
The part about core_complex_logic that bugs me most is that I’m making a function public that could be private purely for testing purposes. Sometimes I live with that tradeoff, but if it became a concern I could move the core decision logic into a new module with @moduledoc false (which isn’t perfect either).
This could be taken even further. Rather than returning a two-tuple, return some sort of a “command” struct inspired by examples such as Ecto.Changeset or Req.Request plus a module that knows how to “execute” the “command”.






















