5 ms·
More importantly generic code is almost inevitably bloated, less efficient, and more complex compared to code which only attempts to solve the actual problem at
by copx 9y ago
More importantly generic code is almost inevitably bloated, less efficient, and more complex compared to code which only attempts to solve the actual problem at hand, as opposed to attempting to provide a generic solution for all imagined use cases.
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.
- andrepd 9y agoHow do you mean, suboptimal? The fully generic version is 2 lines of initialization more than the restricted version. Is 2 lines not an accepted tradeoff for being able to change the engine, and crucially, to draw numbers from any distribution you want? 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.
- deleted 9y ago[deleted]
- gp7 9y agoC++'s rng is bad because it's very easy to set them up incorrectly, and people mostly do. See http://www.pcg-random.org/posts/cpp-seeding-surprises.html http://www.pcg-random.org/posts/cpp-seeding-surprises.html
- copx 9y ago>How do you mean, suboptimal? The fully generic version is 2 lines of initialization more than the restricted version. 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.
- andrepd 9y agoThat is not a 200% increase in code size. It's a fixed two more lines of code, that may or may not translate into a bigger binary (if correctly implemented it probably doesn't, even if it increases compile time a bit). I mean that's just silly. I'll assume you write everything in one-liners, right, since for some reason you find it important to minimise the arbitrary measure of lines of code?
- chriswarbo 9y agoI tend to distinguish between two uses of the word "generic": code can be "generic" by providing hooks/parameters to override every decision, which leads to complexity and bloat, as you mention. 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.
- carussell 9y agoExcept using only car and cdr does imply complexity—linear complexity; instead of being a constant-time operation, access to an arbitrary element in a list is O(n) with respect to the length of the list.
- chriswarbo 9y agoAh yes, I didn't mean "complexity" as "computational complexity"; I use is but rather as how sprawling and difficult to understand the code must be (we could regard this formally using something like Kolmogorov complexity, but I meant it informally). 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.
- EpicEng 9y agoI agree with you in spirit, but yours was a poor example given the known issues with rand() and the fact that the C++ version is not at all difficult to use and accounts for rand()'s warts. Besides, writing a standard library is a great example of when you _do_ need to account for pretty much every use case and write sufficiently generic API's.