Economy Of Expression

A very important criterion for evaluating the suitability of a programming language is how economically the notation reflects the intent of the programmer.

I don't know who coined the term, but I learnt it from one of the great SoftwareExpertsIpersonallyRespect, RobPike. (Anyone know the exact quote in which he gives his opinion of how pointer arithmetic in CeeLanguage helps its EconomyOfExpression?)

Of course not all expressions are created equal, and various languages choose different trade-offs with regards to EconomyOfExpression. Some expressions are more economical in one language while more tenuous in another. One of the most obvious examples are arrays. In the AlgolFamily of languages, you'll see this notation in any algorithm involving arrays:

  x=a[i];
  a[i]=y;
In SchemeLanguage one sees it as:
  (set! x (vector-ref a i))
  (vector-set! a i y)
Of course, the Lisp family is optimized for the economy of EssExpressions rather than any expression. :) But, joking aside, the problem which is also present in other functional languages reflects the fact that Lisp/Scheme programmers and functional programmers in general are less likely to use arrays, and more likely to use LinkedLists. These are fine as far as EconomyOfExpression is concerned, but are not as fine as far as EconomyOfExecution: they generate more garbage (to collect) and object creation overhead.

It is hard to tell from that example alone whether the total app would be more compact. Being compact in one area may not necessarily translate to another area. The meta abilities of EssExpressions may win the war even though it loses that particular battle. I stamp this "insufficient info". One would probably have to see the whole app or several apps. Note that a language that uses dot syntax for maps (and maybe uses maps and objects interchangeably) may have:

 x = a.i
 a.i = y

If we allow nested maps, one can do:

 x = a.i.j.k
 a.i.j.k = x

(There's the distinction between subscript value and variables as subscripts that is not addressed here. [digging up related link....])


It is my observation that EconomyOfExpression is best achieved by creating a DomainSpecificLanguage or domain-specific API/interfaces/utilities (ApiIsLanguage). I've never seen a realistic code example of one paradigm clearly beating the pants of off another in terms of code volume, at least not related to my domain. Most of the expressive economy I've seen comes from fitting the domain well, and some participants in ChallengeSixVersusFpDiscussion seem to at least partly agree. Of course EconomyOfExpression is not the only factor in town.

For example, a custom query language may be more compact for a given project than SQL. However, one must weigh that against tossing a decent standard (SQL). A future different maintenance programmer would probably prefer that SQL be used instead of a custom query language because he/she doesn't have to learn a new query language. (SQL could use some adjustments, but that's another story.)

Flexibility to future change also tends to counter EOE. Abstractions wound too tight are often not as flexible as looser ones, requiring complete overhauls if requirements no longer fit the tight abstraction.

--top


While DomainSpecificLanguage has some promise, bear in mind that its most common application is often to express domain models. In a DomainSpecificLanguage a 'domain model' is a collection of domain data (who, what, when, where, why) minus the axioms and assumptions of this model (which tend to be 'assumed' from the context: the language in which you are stating the domain model). The basic idea is that one may then systematically explore it and make predictions (aka 'running' the model). In practice, this usually requires constructing a processor for the DomainSpecificLanguage, an implementation, a task that is usually error-prone and difficult, especially if one wishes to achieve modularity or efficiency. EconomyOfExpression only happens on the assumption that some other dude has already suffered through making that processor.

Uh... and before I start a LaynesLaw war, just to be clear, I would not call "DomainSpecificLanguage" a sublanguage whose domain is some aspect of constructing a computation, even if said language is not, by itself, a complete GeneralPurposeProgrammingLanguage. I.e. Regular Expressions, Type Descriptors, DataflowProgramming specifications, and DataManipulation languages (like SQL or LINQ or XPATH), are not what I am discussing above. I'm talking about 'DomainSpecificLanguage' in the stronger sense of 'this is a set of terminology that might be used to describe a situation by a domain expert... when the domain expert is not an expert of programming describing the aspects of a program' - e.g. a language to describe a blueprint of a house, a set of business contracts and business rules, parts of a SceneGraph, a DocumentObjectModel, tasks of a mission, a choreographed dance, music, a script for actors on a stage, obstacles on a map, describing diagnostic symptoms, etc.

I posit that DomainSpecificLanguage, in the sense described above, should not be the mechanism by which 'programmers' achieve EconomyOfExpression. Writing code in a DomainSpecificLanguage is the job of end-users, data-entry personnel, chemists and physicists and scientists and doctors and artists and musicians and people doing taxes. Entry of domain models via DomainSpecificLanguage might be supported by forms and feedback or might be a poorly documented configuration data in a PlainText file... but is ultimately input into a 'processor' whose implementation is the task of a 'programmer' - in essence, someone whose expertise is implementing a DomainSpecificLanguage (one might consider 'interaction' an implementation of an interactive language). The closest a DomainSpecificLanguage should come to expressing computation concerns is describing that certain domain events happen in parallel while others happen sequentially. Designing a DomainSpecificLanguage is a task that will probably require both a LanguageDesigner and an expert in the domain... likely both in the same brain.

For the programmer, the 'domain' is computation. That's a big domain, and is filled with dualities (data and process, ObjectVsModel, mutable variables and immutable values, calculation and communication, trigger and event, state and function, hard and soft, policy and mechanism, configuration and definition, type and representation), concerns (functionality, safety, security, performance, distribution, delay and disruption tolerance, hardware failure, graceful degradation, changing requirements, runtime upgrade), and engineering issues (code management (OnceAndOnlyOnce, EconomyOfExpression), configuration management (for each user of a project), project breakdown and composition, gain and loss of personnel, scheduling and funding, maintenance, transitioning systems, training people in them, and demonstrating improvement). The goal of a LanguageDesigner for a GeneralPurposeProgrammingLanguage is to support concerns without causing harm to engineering issues.

As a LanguageDesigner and ProgrammingLanguageTheory enthusiast, I have observed that the dualities alone will ultimately doom any 'EverythingIsa BigIdea language'. Supporting only half of a duality will make that half really easy at the cost of making the other half completely counter-intuitive. 'EverythingIsa object' will be brought to its knees when it comes expressing data that reflects the world outside. 'EverythingIsa rule' will have difficulty expressing ConfigurableModularity that is achieved by use of exchangeable objects with predefined interfaces. Further, I have observed that blending a duality also has problems: functional programming languages that are 'impure' lose many of the potential performance, safety, security, etc. benefits one might have achieved by separating them (reordering computations, versioning and transactions, migration of functions in a distributed system, and many more detailed in a rant on IdealProgrammingLanguage). Blending trigger and event prevents reuse of either. Blending hard and soft results in the elegant simplicity of CeePlusPlus combined with the blazing speed of SmallTalk =).

So I'm armed with two observations (1) both sides of any given duality need support lest you solve only half the programming problem, and (2) blending them often results in a TuringTarpit - the loss of discernible language features other than being TuringComplete. One might call these SymmetryOfLanguage and YinYangPrinciple, respectively. Patterns that follow these combined principles include such gems as AlternateHardAndSoftLayers, SeparatePolicyFromMechanism?, SeparateCalculationAndIo?, ObserverPattern (which separates trigger from action), and so on. Doing so as a DesignPattern keeps pieces of the design simple and reusable... and DesignPatterns indicate a MissingFeatureSmell.

Based on these observations, I believe that the means to achieve EconomyOfExpression (and every other SoftwareEngineering benefit) is to identify as many dualities as possible, then, with an eye towards implementability, performance, and other concerns, one should select and divide these dualities at the language level... being careful that the language supplies both sides of each duality, and providing a mechanism for them to interweave. Examples:

There is no DomainSpecificLanguage for this purpose... there is no way to model the who, what, when, where, why, and how of a 'program' without a GeneralPurposeProgrammingLanguage. But these 'separations' ultimately result in a bunch of sublanguages - possibly simply trios of keywords that express dualities, none of which are a model of the program but each of which, in an aspect-oriented manner, interweaves to support the construction of a complete program.

By these principles we escape the TuringTarpit. By these principles we achieve EconomyOfExpression.

Can I request that you provide some examples, or work with existing examples from this wiki to illustrate this?

There is a problem with 'principles': they aren't algorithms. They might help LanguageDesigners diagnose LanguageSmells and even suggest approach for repair, but they do not allow one to automate language design and they do not tell LanguageDesigners how to best fix the smells... i.e. precisely how to achieve the duality, and how the dual components should be interleaved, are still difficult tasks that belong to the LanguageDesigner. To the degree the above principles help, I provided a few examples above (a group of four bullet points). They aide with EconomyOfExpression mostly because it is usually roundabout, obtuse, indirect, and inefficient to 'express' (via hand-implementation) any given unsupported duality in a language, such as expressing DependencyInjection and open functions in a 'modular' language that only allows modules to 'export'.


Being that principles often conflict with each other, do you agree that they should be road-tested, or at least an approximation of road-testing? All tools should be tested, if possible.

Principles are tested first by their application to known data to look for conflicts and evidence in the scope of their desired application, then again by their actual application to see whether they succeeded in making useful predictions or suggesting solutions. I've already provided the former testing. The latter you might call 'road testing' and in practice can only happen after a principle is being promoted. Anyhow, I'm curious which principles you think conflict with those listed above.

It's not quite specific enough. It almost sounds to me like "marketing speak" because I cannot find enough concrete statements to latch onto without heavy risk of categorizing things different than what you intended. I'm not sure where to start.

You asked a very broad question (should 'principles' be road-tested?). Why does it now sound like you expect a more specific answer?

Principles are tested by the same means as all other hypotheses: filter first by looking into the past for observed evidence and conflicts, and perhaps by looking at existing accepted models for evidence and conflicts. You can also run a few small-scale tests, e.g. by following a principle through scenarios and use-cases and seeing if they result in the expected observations, but ultimately that is a point test highly subject to confirmation bias. To 'road test' a principle that has made it through the initial filters, you really must promote it, teach it, encourage people to apply it... then observe, likely a decade or two on, looking for conflict and success and continued adoption. The idea behind this is simple.

If you believe that tools, such as principles, can be 'road-tested' before they are 'road-promoted' in the marketing and economic sense, then you're far more an idealist than I am. The short answer is: Yes, I believe principles should be road-tested... but it must be done by their application, and it must be done in the real world.


EditText of this page (last edited August 10, 2012) or FindPage with title or text search