Precise abstractions apply to exactly the right types and D allows to check for that.
I’m not super-convinced about the last few iterations being more or less “precise”.
I think a more important distinction between ‘vague abstractions’ and ‘precise abstractions’ is when we get things like DocumentManager or ServiceInitialiser, which “abstract” by lumping together chunks of possibly-related code, which introduce new vocabulary but which have no precise meaning (e.g. it would be hard to write a non-trivial equation which is obeyed by every ServiceInitialiser). We can’t talk about, say, a DocumentManager in its own terms; we always have to keep in mind what roles and responsibilities happen to have been put in it, and which haven’t.
In contrast, something like User is more precise: we can confidently say things like emailOf(x) = emailOf(y) implies x = y, or makeAdmin(makeAdmin(x)) = makeAdmin(x), etc. User gives us a meaningful, useful vocabulary in which to discuss and think; rather than merely pointing us to the right place to look in the source code.
emailOf(x) = emailOf(y) implies x = y
makeAdmin(makeAdmin(x)) = makeAdmin(x)
(I tried to pick examples which are intentionally more “business like”, rather than “math like” abstractions like monoids, rings, etc.)
I think one problem with such business examples is that they are specified less precisely than math like stuff. What exactly is a document or a service? I have also come across systems where email addresses were not unique, because couples had two entries but a shared email address.
It requires rigorous spec to be precise. Math delivers that. Business does not.