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.

Leave a Reply

Your email address will not be published. Required fields are marked *