Library patterns: Multiple levels of abstraction
tomasp.net
tomasp.net
As you can infer from my use of scare quotes, I side with the author on this after accumulating repeated scar tissue.
Won't you be stuck exposing those APIs for the lifetime of the product or risk breaking backwards compatibility?
What if you could make the code significantly faster by changing the parameters you pass in to an internal function? What if you discover a bug that you can only change by changing a method's signature? What if you just want to refactor? Doesn't exposing your internals prevent you from doing these things?
(Note, haven't read the article yet).
A real revelation for me over the last year of using nodejs more and more, is that every component can use its own dependencies, at exactly the versions it requires. So, when you add to an API, bump minor. When you take away, bump major. Either way, you can bump patch for bug fixes, on any major-minor combo, whenever you want. So "stuck" isn't really possible any more.
"It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures."
I think OO tends to go with the 10 functions on 10 data structures, whereas the FP goes with 100 on 1. Once you are able to abstract your API in such a way, the work of creating these 'multiple levels of abstractions' becomes an exercise for the reader, because of the composability of the API.
I'd say this isn't quite true. FP is full of sophisticated ways to hide information. Done correctly it is a giant boon to composability—often done under the name "modularity".
The core of this function resides in type quantification—existential or universal. Any polymorphic type system will thus have a small amount of this. In particular, the fact that a function is parametric in a type is exactly an expression of the fact that a function remains ignorant to all details of the type actually chosen:
id :: a -> a -- a function with this type cannot examine its argument
id x = x
More tangibly, ML-family languages (but not quite F#, I don't think) have the ability to create abstract types by exporting module signatures which are ignorant to the actual type embodied in the struct. Again, this is a powerful technique for increasing composability.The core idea in each case is that when you build interfaces which demand exactly and only what they truly need from their impementers then you allow the maximal variety of implementers possible. You also allow the implementer to be exchanged---your use of this interface thus composes in more contexts.