Of all things, a sci-fi novel recently got me thinking about the importance of notation and how it influences our thinking. In short, syntax length impacts the kinds and sophistication of ideas.
The sci-fi book was Canticle for Leibowitz. Some scholars of a regressed humanity discover an ancient document with sum notation. They marvel at the concise representation of so much information and wonder at all the ideas they hadn’t even considered that could be explored.
I’ve long learned that we should program into langauges in not in them. In other words, create your design based on your needs and figure out how the language can achieve it instead of basing your design on available language features. I also have long loved static language. Popular static languages like Java and C# have developed a reputation of lots of “ceremony” or unnecessary syntax to express an idea. Many have turned to dynamic languages because of this. This is a false dichotomy, however. Static languages can also have low ceremony. F# is a great example.
Still, the concern over ceremony is valid.
The longer or more effortful an idea is to express, the less likely we are to express it. Just look at natural languages. Most anything we say often quickly gets a shortened form. However, programmers can’t abbreviate syntax like we do with words. Instead, ideas with inconvenient syntax don’t get expressed as often. Longer syntax can also take more time to understand, and understandability is one of the most important factors for long-term maintainability of a system.
The Sapir-Whorf hypothesis suggest that the language we use influences and may be limit the thoughts humans have.
This certainly seems to be important in math and scientific fields. The effectiveness of notation is often related to the growth of ideas. Ideas can grow more complex by reducing current ideas into more terse communication. I’m thinking of examples like chemical notation. Using prose to describe the states and relationships of atoms is much less efficient than the notation we use to show elements and bonds. This itself requires the terse element notation of the periodic table. We’ve also taken chemical notation further with special representations for common combinations, like Benzine rings. Each of these steps in notation cemented a shared concept in a way that enables more efficient communication, which in turn enables us to reason about more complex ideas.
To clarify, I don’t think shorter is always better. Many code understandability violations are made in the name of conciseness. Understandability is of first importance. Conciseness can be an aid understandability, and clear concise notation can lead to new ideas and expressions. However, never be more concise than clear.
Here are a few examples where short syntax has effected how I program.
F# has union types, which represent a set of alternatives that don’t necessarily have the same data.
C# has no concept of unions, and the intent of the alternatives is not very clear with traditional class syntax. Thus, I rarely used union types in my C# even though I like using them in F#.
However, C# introduced positional records which allow a concise approximation of union types. Now I often use union-like records in C#.
Effective pattern matching is also key to concisely consuming union-likes. C# previous would have required a bunch of type checks and casting. Now it can be done like this
I have a whole post about unions in C#.
In F#, there is an operator for composing two functions. That is, to create one new function that does the same work as the two original functions.
In F# we can compose three functions together like this.
In C#, achieving just that last line of F# looks like
Here the difference is not just density, but also order. F#’s composition operator allows the composition to be read left to right with no scope tracking. C#’s approach requires the user to read from the inside out of a series of nested scopes.
If we compose multi-argument functions the difference becomes even more significant. F# can partially apply information known beforehand
C# would have to create additional functions to do the same. Something like
F# uses a sophisticated type inference system. As such, function parameters often don’t need any explicit typing but remain strongly typed.
This makes it very simple to write functions that compose other functions.
C# can pass functions as parameters, but infers far fewer types. As such, functions that take other functions tend to be verbose and difficult to read. It takes a much more compelling case for me to write higher-order functions in C#.
This function doesn’t make much sense in C#. It’s not really better than directly using a try-catch.
However in F#, functions like these can be composed into very clean and readable pipelines.
In summary, syntax length matters. Ideas that take more effort to express are less likely to be expressed. This directly impacts what design concepts we leverage. Advances in clear and concise notation also level up our ability to explore new and even more complex ideas.