Static vs Dynamic Typing: When LCP and Conciseness Conflict

Most of the time application of the Limited Construct Principle will result in more concise code. This is because more limited code constructs generally allow for less options and configuration so less information needs to be specified. In these cases application of the LCP is a slam dunk, but in some cases choosing a more limited construct means writing a lot of additional code. Such is the case when we compare dynamic to static typing.

Argument for Static Typing

Static typing is great. It follows the LCP and bountifully reaps the benefits of doing so. Constraining all your data and function inputs/outputs to predefined data structures means it’s easy for the reader of the code to know the data structure and reason about the results. Tooling also benefits greatly. Compilers can prevent many kinds of runtime errors from ever occurring. Editors can provide extensive support with features like auto-complete, refactoring, and code cleaning such as dead code elimination. With sufficiently advanced type systems there’s really nothing you can do with dynamic typing that you can’t do with static typing.

Argument for Dynamic Typing

Dynamic typing is great. I don’t need to write a bunch of code to define my data types and more importantly I don’t need to build a complex hierarchy of interfaces to get functions to be able to work with multiple data types. I can get code running faster and I don’t miss the editor features that much because I have much less code to deal with. Compilers don’t catch all the errors, that’s why we write tests, and our tests will catch anything that the compiler would have caught anyway.

A Note on Coding Principles vs Conciseness

People seem to be widely dispersed on this spectrum. In any debate on this subject it’s important to remember our root values in writing software.

  1. How well does the code communicate your intentions to the computer (measure by accuracy and performance and resource metrics)
  2. How well does the code communicate your intentions to the reader of the code (measured in time reading to understand, time it takes to change)

If a coding principle is not helping you in either area then it should not be applied in that situation. Further these two values have to be balanced against the cost which is measured in development time.

Another point to note is that most coding principles really provide most of their value in larger contexts. For example it’s more important to have a descriptively named argument to a stand-alone function than it is to do so with an anonymous variable to a function operating on a descriptively named collection. As the context shrinks we can favor concision more which can potentially have a compounding effect of further shrinking the context.

Static typing helps us with both 1 and 2 while dynamic typing by allowing us to write less code helps us with 2 and lowers our cost. So what should we do?

Can Static Typing Just Be Made More Concise?

I think there’s definitely some value here, although in a language like C# we’ve already come a really long way from something like the old Java declare a private variable and have accessor methods. I really don’t think there’s a lot of value to be gained here if you look at type declarations in F# I don’t think you can get any more simplistic than this:

type ComplexNumber = { real: float; imaginary: float }

Typing a la carte?

Another option might be to support statically defined and dynamic data structures. This would allow you to use statically defined types in places where it provides the most value like on higher level interfaces or common utility functions but where you don’t feel it covers the cost. I’ve seen limited success with this with anonymous types and dynamics in C#. They often don’t play well though in a language designed to be statically typed. Clojure is the opposite being dynamically typed by default but has some libraries such as Prismatic/Schema and Core.Typed that offer defines data types with type checking.

Inferred types?

It is possible to have defined types and then infer what type is being used by assignments or operations being performed. C# does some of this, F# uses it more extensively so that even thought it’s a statically typed language it lets you write code like this

let add x y = x + y

and then infers that x and y must be some kind of number and that function is of type number -> number. This dramatically reduces the cost associated with static typing.

Conclusion

C# types feel too heavy. Clojure’s dynamic typing makes coding feel fast and easy but trying to figure out what the structure of the data is when reading code in a large application can be a real pain point. After writing this I really want to try out Clojure with Prismatic/Schema and play around with F# because of it’s type system.

The Limited Construct Principle

I would like to present a new guiding principle for software development along the lines of the SOLID principles. It’s not really a new principle because argue from this principle often, however I am not aware of it being stated anywhere explicitly. I call it the Limited Construct Principle. Simply put it means that given a choice between multiple programming constructs you should choose the most limited, one that can accomplish the task. Why? Well the reasoning is that a more limited construct will always communicate your intention more clearly than a less limited one. Let’s look at some examples

GOTO vs Structured Programming

The first one I would like to address is the source of this blog post by Robert Martin. The GOTO statement can easily perform the function of if-then-else, while loops, for loops, function calls etc. However seeing the GOTO statement gives the reader no indication of what the intention is. No indication of what form of flow control is coming next. Furthermore since all forms of flow control are possible with the use of structured programming and all individual structured programming constructs are more limited than GOTO the Limited Construct Principle would indicate that we should never use GOTO. This is in fact strongly recommended.

Access Modifiers

Many languages allow the user to specify whether a variable is public or private to the scope of a class/namespace. Since private is more limited following LCP would indicate that we should always make our variables private unless we need them to be public. Stack overflow agrees. In addition to communicating your intention to human readers of the code the access modifiers also communicate intention to the computer allowing your editor to tell you a private variable is never used and can be safely deleted or simply not include that code when compiling. This concept is so powerful that even in language that don’t have public/private as first class concepts people have figured out a way to effectively implement it using closures and it is now considered best practice.

Collection Operations

In C# we can use a for statement or a foreach statement to iterate over a collection. The foreach statement is more limited because it can only operate on a single item in the collection at a time so we would generally choose the foreach statement and when we choose the for loop it would be an indication we want to do something more complicated (order probably matters!). However if we’re performing some of the more frequent collection operations we should use the LINQ methods that are even more limited and specific such as Select for transformation and Where for filtering. This indicates our intention. Note that the principle is still applicable even if the language itself does not strictly enforce the limitations (although it’s still better if it does). For example it is possible to keep track of the previous item in a foreach loop or mutate the objects in a collection during a where but you shouldn’t because the construct you chose indicated you wouldn’t.

Immutable Data Constructs

Clearly immutable data is a more limited construct than mutable data so LCP would indicate that we should always use an immutable data construct unless it is necessary for the data to change. Many functional languages have constructs that support this concept, in F# all data is immutable unless you specify otherwise, similarly Clojure provides specific constructs for different forms of mutability. Immutable data immediately communicates a great deal to any reader of your code. For example if you pass immutable data into a function you know that the after the function you will still have the same piece of data. However although it is possible to model immutable data structures in C# I do not generally choose to do so, primarily this is because C# makes working with immutable data a poor choice in many situations both because of performance concerns and ugly verbose syntax. Thus the application of LCP should be balanced against any additional programmer work or extra code required. As much as I would prefer to use immutable data in C# the language pushes me not to.

If we follow Limited Construct Principle consistently we will make our code easier to follow and reason about. Limited constructs will make it more immediately apparent how they are going to be used, easing the mental burden of the reader. Then when we use more capable constructs we will be clearly communicating that those capabilities are intended to be used.