Friday, June 22, 2012

Puns in Programming Language Design

Henri Bergson defined a pun as a text in which "two different sets of ideas are expressed, and we are confronted with only one series of words."  In natural language, puns are often used for comedy, but a skilled author can also use them for powerful literary effect.  For example, the opening lines of Richard III: "Now is the winter of our discontent / Made glorious summer by this sun of York" which has three relevant meanings.  Or John's pun as he recounts Jesus's words, "Except a man be born again, he cannot see the kingdom of God" in which "again" is a Greek word meaning both "again" and "from above."  In both cases, a pun allows the author to convey his full meaning with fewer words than otherwise possible.

Puns require both a talented author (to create the pun) and a talented reader (to understand the pun).  I admit to missing one of Shakespeare's meanings the first several times I read Richard III's opening lines.  As with all linguistic constructs, it offers benefits and costs.  Those must be weighed in each potential use case.

Puns are not unique to natural language.  They exist in optical illusions such as the Necker Cube.  Many programming languages allow sufficient, intentional ambiguity for productive puns.

Dynamic Type

Consider the following JavaScript function which adds elements of a list to an initial value.

One can invoke the function with integer, float or string values and obtain useful results. Dynamic types and an overloaded + operator provide the necessary ambiguity.  This single function definition gives three useful procedures as a result.  Most statically typed languages prohibit this pun by requiring type annotations on variables and parameters.

A talented JavaScript engine might compile the above code into completely different machine language depending on the context in which it's used, the values available at runtime and the processor architecture on which the code is running.  Just as natural language puns require a talented reader, programming language puns require a talented compiler.

Dynamic languages like Erlang and Dart embrace the ambiguity of dynamic types by adopting smart tools like dialyzer and dart_analyzer, respectively.

Parametric Polymorphism

Most functional languages let one write a generic map function like this:

The function is a pun with meaning across all list types.  The compiler infers types where necessary to ensure safety.  The absence of type annotations provides the necessary ambiguity, so dynamic languages support similar puns, although without compile-time type safety assurances.

Java and C# allow similar puns with generics.

Logic Programming

Languages like Prolog and Mercury offer puns through ambiguity in constructors and pattern matching.  For example, a single append definition creates procedures for combining two lists to make a third, removing a list prefix, removing a list suffix and generating all combinations of two lists which, when combined, produce a third:

One can use similar constructs to create punning predicates that split/join strings, parse/serialize data structures, etc.

In Mercury, goal order is unimportant which allows further puns in which the compiler reorders goals to achieve higher performance, lower memory usage or automatic parallelism.  In each case, a single textual definition provides multiple procedural meanings.

Futures and Laziness

There's a related class of puns in which meaning is the same but a programmer is intentionally ambiguous about an important implementation detail.  For example, Alice ML provides futures for the results of concurrent computations.  In Alice, a future is syntactically identical to a variable.  When one writes a function of a variable, it works on values, completed futures and incomplete futures.  This lets APIs change their internal implementation with respect to concurrency without breaking encapsulation and gives the compiler freedom to schedule concurrent threads as it sees fit.

Haskell's pervasive laziness is in a similar vein.  A variable could represent a value or a thunk which, when needed, computes a value.  A single function definition handles both.  Mailing list threads about overcoming Haskell's laziness remind us that in some cases ambiguity is a pun too far.

Psychology of Puns

Most software developers have strong aesthetic preferences about their languages and tools.  Witness the vi-emacs wars, endless debates about semicolons or brace placement and arguments about static vs dynamic typing.  In these scenarios, there is no right answer.  There are only programmers more or less comfortable with various constructs.

Programming language puns and their linguistic ambiguity are no different.  In this case, psychologists even have a name for it: ambiguity tolerance.  Some people want firm rules and an environment that resolves into clear, precise answers (One True Way).  Others thrive on uncertainty and broad choices (TMTOWTDI).  Programmers bring this psychology with them when they design and choose languages.

Fortunately, I think there's room for compromise.  By making certain language constructs optional and relying on smart compilers, programmers can either be ambiguous or precise, depending on their preference.  The language can stay with them as their preferences evolve.  There's a lot of interest in optional static typing (Perl 6, Python, Newspeak, Dart, Erlang, etc).  I'd like to see further exploration of optional annotations and compilers that can usefully resolve programming puns.

No comments: