It's not easy to write generic code.
It's not easy to write generic code.
A simple example would be a PRNG. Specialized version:
int dice_roll = random(1,6);
Generic version (this is from C++'s standard lib): std::default_random_engine generator;
std::uniform_int_distribution<int> distribution(1,6);
int dice_roll = distribution(generator);
The specialized version does not cover non-uniform distributions, nor the use case where you need multiple PRNGs with independent states.. and is much simpler and more straight-forward because of that.Often generic solutions are the best trade-off in the end, but one should not assume that more generic is automatically better.
I think the reliance of modern programmers on libraries which offer highly generic (and thus almost inevitably sub-optimal) solutions is one of the primary reasons for software boat.
Furthermore I bet the generic, templated version is as small and fast as a naive implementation. It's all templates so it's all static code generation.
I.e. a 200% increase in code size! Now imagine that throughout the entire code base.
> Is 2 lines not an accepted tradeoff for being able to change the engine, and crucially, to draw numbers from any distribution you want?
It is a complete waste if you don't need that functionality. That is the point. Generic solutions solve problems you don't even have, and that comes at a price.
>Furthermore I bet the generic, templated version is as small and fast as a naive implementation. It's all templates so it's all static code generation.
You kinda missed the point. I did not even specify how random(a,b) was implemented, so making statements about the speed of the compiled code makes no sense here.
C++'s templates are another nice example of the cost of generic code, though. They are one of the primary reasons why compiling C++ is so slow / resource intense. They have to be instantiated at compile-time again and again and again.. which is a non-trivial process, much slower to compile than a plain non-generic function call. Also they are historically infamous for producing hard to understand error messages.
On the other hand, code can also be "generic" by avoiding decisions, parameters and special-cases. An example of this is Lisp's use of cons cells to represent data: regardless of the contents, and what it's meant to represent, we can always use `car` and `cdr`; they're "generic".
Compare this to a typical OO approach, like a `Customer` object with a `name` and `dateOfBirth`: we can't access those fields without either writing those particular strings (`name` and `dateOfBirth`) in our code; or use some complicated reflection approach to look up the names then use those to look up the data.
Of course, it depends completely on the context whether to use special-purpose datastructures (e.g. custom classes to model a domain) or generic ones (pairs, lists, maps, etc.), but generic doesn't always imply bloat and complexity.
One advantage of "specific" (as opposed to "generic") code, like explicit constructor/destructor pairs (e.g. named properties and accessors), is that by forcing client code to choose one particular, specific accessor, we have a lot more static information to use for optimisation; i.e. we've encapsulated the implementation behind an opaque interface, which we're free to implement in whichever way makes most efficient use of the resources we care about.