This is a theory which says that if you push down the complexity in one part of a language or tool, there is a compensation which increases the complexity of another part of the language or tool. This is metaphorically compared to a water-bed, in which pressing down in one spot raises other spots.
(Unlike air, water does not compress very well. This is partly why hydraulic systems are so useful: you push the piston at one end of the closed system and get almost the same displacement far away from the piston. This reduces the quantity of motors needed and allows the "business end" of the tool to not need to be bulked-down by the engine itself.)
No language is complex in every way, nor is it completely simple, to make it useful one must not make it entirely and only simple or complex. To do so will remove its potency.
Complexity and usefulness are related, in that system constructs which are extremely complex, hard to build and sometimes almost incomprehensible to the average layman, can be simply and widely useful.
While one may not understand something as complex as electricity, what it is and how it is generated and transmitted, one may utilize it simply by plugging an appliance into a wall socket. This can be said to be true of computer languages, operating systems and the computer using them.
[WaterbedTheory is] Evidently false with languages, as a language may have an associated library/framework which hides the complexity completely. For exaxmple, .NET is shipped with a framework that implements .Sort() on various objects. The complexity in a .NET language of sorting those objects is COMPLETELY hidden (calling .Sort vs rolling your own). Thus 'language' is the wrong term. At the byte-level, the complexity remains, but at the (high) language level, it is gone.
[I'm certain .NET makes hiding some forms of complexity easier, but there are almost always MissingFeatureSmells. Some forms of complexity can't be hidden so easily. How easy does .NET make it to hide complexity related to memory concurrency protection, memory versioning and FirstClassUndo, backtracking, logic programming and function inversion (result->likely arguments), reactive programming, transparent persistence or distribution, security and privilege management, unit testing, etc.? Peruse the list of KeyLanguageFeatures before claiming that a given language allows you, even with powerful libraries, to "hide the complexity completely". Even Greenspunning the task by implementing part of some other language doesn't help if it introduces more complexity on integrating two or more different 'language' frameworks.]
The "sort" example is a confirmation of GreencoddsTenthRuleOfProgramming. Collection-oriented idioms are continually and constantly needed, and tools/languages that don't supply or facilitate them will just cause unnecessary pain. However, I'm curious how every object collection can "inherit" a sort method and other collection-oriented idioms without breaking "true" implementation-swappable encapsulation. This has been a sore spot for OO and collection-orientation integration. --top
RE: This is a theory which says that if you push down the complexity in one part of a language, there is a compensation which increases the complexity of another part of the language.
It seems this would be best mitigated by allowing manipulation of the language... so you can 'push down' complexity on whichever part you're using at the moment. Macros are one way to do this, but if macros are complex or difficult to create without bugs, then there's a DiscontinuitySpike whenever it's time to manipulate the language for the domain. So, perhaps, the 'ideal' programming language would work first to massively simplify the extension of its language.
This is an exact example of this rule at work: If you try to push down the complexity everywhere the complexity of the macro system increases.
The rule above says "one part" -> "another part", not "every part" -> "one part", so it isn't a very 'exact' example. ;-). That aside, the above statement implies more that the macro system needs to be flexible, not particularly complex. Indeed, it states the opposite: that macros or syntax extension shouldn't be difficult or complex to utilize (lest they become the domain of experts only, like in Lisp). Whether flexibility or simplicity in a macro system leads to complexity in another part of the language (perhaps its implementation), is deserving of its own discussion. Whether complexity of implementation counts as "part of" the complexity of a language is also deserving of its own discussion.
[Obviously a macro system will be more complex to implement than no macro system, but that implementation complexity could (conceivably) fully pay for itself, complexity-wise, via use of the macro system to implement parts of the language and its standard libraries (i.e. the 'standard' syntactic sugar).]
[Perhaps the real cost of heavy use of metaprogramming is in the learning burden. If people who DoTheSimplestThingThatCouldPossiblyWork are taking advantage of a simple macro systems, then the 'syntax' of the language, and the semantics of statements within it, can be expected to diverge on project-to-project basis (at least for some parts of each project). The 'cost' here is essentially that everyone needs to learn a new language when coming aboard new projects. And if there are heavy code-walking macros or other metaprogramming that are non-compatible, that might also split the developer community. Essentially, each project becomes its own language.]
[OTOH, that might not be a 'new' problem. I've been on enough projects to know that one is essentially learning a new 'language' for each library and framework in addition to the project code itself. Programming, in essence, really boils down to developing the language in which to clearly state the program. We may be avoiding a great deal of AccidentalComplexity by simply admitting to it and providing the MetaProgramming facilities. It seems the WaterbedTheory doesn't touch on issues of AccidentalComplexity vs. EssentialComplexity; perhaps it should.]
In a language, assuming TuringCompleteness, it isn't the inability to express an idea, but rather the inability to express an idea efficiently or directly. Awkward greenspunning, repetition of ideas rather than OnceAndOnlyOnce expression of them, incompatibilities between frameworks due to inability to represent or share assumptions, etc. are problems faced often by programming languages.
I expect it possible to squash most AccidentalComplexity in a manner that, in a global sense, reduces complexity. But this may come at a cost for 'local' complexity... e.g. for a single project, it may be simpler to simply deal with the awkward and indirect expression rather than upgrading the language to make said expressions less awkward. There is something of a bulk economy factor (NashEquilibrium) going on here - if you only use an awkward expression once or twice per project (such as writing up all that boiler-plate code around 'int main()'), then it is difficult to justify paying to improve it for everyone. DoTheSimplestThingThatCouldPossiblyWork proscribes such an effort.
I'm speculating, but I expect that we'll only see certain forms of complexity-reductions when we have facilities to really perform cross-project refactoring that crosses dozens or hundreds of disparate projects. Something like WikiIde might lead in that direction. And I further suspect that the base language will need to be very flexible or mutable in order to express the language in which to express the programs.
In some ways the WaterbedTheory relates to EssentialComplexity. Obviously you can't be rid of EssentialComplexity - the best you can do is make it someone else's problem [this is a 'best' thing?!]. Apropos, doing so is called LeakyAbstraction. Push down all simple parts and "poke a hole" in what pops up, deferring the complexity to be defined in a separate layer of concern.
This is such a catchy "real world" and concise metaphor. Kudos to the originator.