3 ms·
How does a lack of "critical built-in data structures" (as opposted to libraries) lead to poor design? C has a ridiculously small set of built-in data structur
by sdp 18y ago
How does a lack of "critical built-in data structures" (as opposted to libraries) lead to poor design?
C has a ridiculously small set of built-in data structures, but the unix core tools written in C are very elegantly designed.
- frig 18y agoEh, don't read more into an argument than is really called for. The crux of the above argument is usually something like: In most nontrivial programs you'll wind up with a lot of "sophisticated" code that performs some task but has a ton of parameters and behaviors it'd be at least theoretically useful to be able to tune or override at some specific point-of-use of said code. Sometimes the right decision is to just hard-code in all the non-essential parameters to sensible defaults, and do only what you need; in places where this approach is warranted ("keep it simple, and only do what you need to"), Java is as annoying to use as ever but doesn't do much to shoot you in the foot beyond being more verbose than it needs to be. It's also sometimes the case that the right decision is to make the code as fully-articulated-as-possible: as many parameters as possible are tunable, and as many behaviors as possible can be overridden. We'll call this the "complicated" scenario. In the "complicated" scenario, you have at least the following choices for architecture: - approach 1: a tower of interfaces and base classes; each interface+base class combination augments or extends some of the behavior or adds or removes points-of-articulation from the layer beneath it. Once you've built the tower, whenever you get to a point where you need to call on this functionality you pick the appropriate item from the tower, subclass+augment its behavior further (if necessary), and go to town. - approach 2: similar to 1, but make heavy use of reflection (at least at certain points) to try and achieve some level of genericity (in the core methods and algorithms) and to make the implementation less dependent (at time of coding) on the specifics of any particular interface implementation. - approach 3: have methods that're like void openConnection(IConnectionSpecification connectionSpecification, Map<String,Object> options), and then be smart about how you extract options from the options Map. approach 1 and approach 2 have their problems but fit well into the tools Java gives you. Approach 1 is more suited for writing code that other people will use (ie, call into), whereas approach 2 is more suited for writing code that other people's code will be used in (ie, called) (yes, that's a bit vague, but it's good enough for today). Approach 3 is common in the scripting languages, because it allows for a lot of ad-hoc extension / parameter setting without excessively cluttering the interface. Approach 3 isn't impossible in Java, but the lack of literal syntax for eg maps and lists makes it unwieldy. If it were possible to write something like: - ConnectionBroker.openConnection(cSpec, {"port":80,"username":"username","password":"password"}); you'd see a lot more use of Approach 3 in Java. As-is, you'd have to explicitly construct a Map of the right type, then one-item-and-line-at-a-time populate it with keys and values, and so on, at which point (especially if used often and in a big project) you're better off coding up an IConnnectionOptions interface, etc., and going from there. So it's less: lack of data structures == bad design. It's more: lack of literal syntax for the basic data structures means that it's a royal pain to do certain ad-hoc friendly tasks in an ad-hoc way. Assuming laziness (always a good assumption), given an idiomatic way to handle cases with lots of potential parameters most programmers would handle things the ad-hoc way, and save the "over-engineered" tower-of-interfaces for the situations which are actually necessary (eg: real-honest-enterprisey-software).
- sdp 18y agoThank you for letting me know I should have explained myself more clearly. "Lack of key fundamental data structures, leads to the OOP bandwagon." Lack of built-in data structures -> overuse of OOP "Interfaces and inheritance are good tools but they should be used in moderation. In my project there is the pattern of BaseInterface, BaseClass, MoreCompleteInterface, MoreCompleteClass, ModuleLevelInterface, and ModuleLevelClass all to implement a DAO with nothingg but getters and setters." overuse of OOP -> Bad design Therefore, Lack of built-in data structures -> Bad design
- russell 18y agoExcellent exposition of what I was trying to say. The overarching concept is the ""expressiveness" of a language. Concept is "How easily can I express my goal in the language. A crude measure of the relative expressiveness of two languages is the ratio of the number of tokens in the respective implementations of a program. I remember reading a study a decade ago that ranked C at 1, Java at 2, Python at 5 (roughly). Having hashes and their literals as part of the language makes it more expressive than one where hash is in a library. Look at JavaScript where the literals for arrays are used as a serialization protocol. Expressiveness goes beyond just literals. In python functions are first class objects, so there is no need for the thunk/functor/inner class kludge that you have in Java. Or look at list comprehensions which are way more powerful than for each. The expressiveness shapes how programs are coded. Programmers will use a concise, powerful idiom because it saves time and thinking and the program is shorter. There is a corresponding obligation on the language developer to make sure the expressive idiom is almost always the best way to go.
- frig 18y agoOh certainly, Java's missing a heck of a lot more kinds of expressiveness than just literals; I was just trying to show, mutatis mutandis, what a Java-with-decent-literal-syntax would be like, as compared to the current state of affairs.
- sdp 18y agoThe "crude measure" that you cite which "ranks C at 1, Java at 2, Python at 5 (roughly)" shows that on average, every line of code in Python is worth 5 lines of C. Let assume that this is true. Naturally, this seems to imply that more powerful languages require less code to perform the same task. Python provides a good example of this, but it is not always true. C is not a very expressive language. It is often cited as a portable assembly. Yet, decades of unix/GNU utilities show that powerful programs can be written in small amounts of C, which are combined to build elegant and decoupled systems. Java is more expressive line by line, but its implementation of OOP requires so much boilerplate that it requires no less code than C to represent the same simple design as the C equivalent. Additionally, because each line of Java represents 2 lines of C, the resulting programs are far less efficient. It is important to consider why this true. Java attempts to abstract many of the details of the underlying C implementation, allowing it to be much more expressive. This abstraction allows the higher level programmer to think about business logic rather than pointer arithmetic. However, these abstractions constrain the programmer's ability to write code that the language designer hadn't considered. Of course, you might consider these to be edge cases and that is a fair point. However, crippling the outliers pushes your userbase towards mediocrity. As pg stated: "when you damp oscillations, you lose the high points as well as the low."