Hacker News new | ask | show | jobs
by mrkeen 376 days ago
I did a fair bit of Rust (only single-threaded, quite a few years ago) but it sounds like the dependency inversion principle is popping up again.

DIP, async/await, coloured-functions, io-monad. All cases where the business logic should go in one column (abstract/sync/blue/pure), and your machinery should go in the other column (concrete/async/red/io). Your logic will remain unit-testable that way, regardless of how big or small it gets.

Irritated newcomers will complain about not being able to call in the other direction. So what's a language-designer to do? Attract the largest possible user-base by not getting in the user's way - or filter out frustrated users and have fewer but better codebases out there?

1 comments

Dependency inversion does apply sometimes, but a well-designed library caps async around functions that need it - things that interact with filesystem or network, etc. Those are perfectly testable as-is. Your core business logic is still sync, if you'd like to to be. Async can call sync just fine, and sync can call async, as long as you are willing to admit that you're abstracting away a network call in a unit tests (for some reason).

The typical pattern I enjoy is:

* Sync for the logic

* Async for the job runners b/c I'd like to do other things while it's going.

* Sync or Async as required for the data wrangling (db calls, network, filesystem)

So you have essentially:

1. kick off job, returning a future, by calling an async function.

2. Job is mostly async data wrangling, followed by a sync logic block that's unit tested, followed by an async "save" if required.

3. Either wait for the future, or run the task in the background, or `.await` if you're in an async block.

I'm hand waving away the syntax, but that's the mental model. 99% of code isn't async, and the code that is isn't async for the fun of it.