3 ms·
After a decade of experience developing big systems in C and C++ followed by a decade of developing big systems in Python, my own experience is that static vs.
by jbednar 9y ago
After a decade of experience developing big systems in C and C++ followed by a decade of developing big systems in Python, my own experience is that static vs. dynamic typing isn't actually the issue that normally matters in a big system. Static typing is great for optimizing performance and for having efficient interaction with the underlying system libraries. So the closer your work is to the underlying machine and its types, the more important static typing is. But only a small part of any big system is usually like that, and static typing makes code harder to develop, harder to read, and needlessly limited in scope (overly specifying down to a machine type, when the actual algorithm may be far more general than that). Dynamic typing addresses those concerns, but it makes for a brittle system where each component makes a lot of undocumented assumptions about what might be passed in, with users of those components often just crossing their fingers about what the component might accept. Worse, dynamically typed systems eventually end up accruing pages and pages of hand-written documentation, assertions, and exceptions as people try to make them more well validated against user and external-programmer errors. All that well-intentioned "hardening" inevitably eliminates the readability benefit, except during initial development (when such checking is conveniently skipped). So neither static nor dynamic typing alone makes it easy to build big systems.
What does make it easy to build big systems is semantic typing. I.e., the ability not just to declare a machine type, but to declare the actual assumptions made by any component about its inputs. In some low-level code those assumptions are about machine types (argument i must be an 8-bit integer, and s must be a string). But in other cases the assumptions are about what the argument represents (argument s is a string denoting a filename (not just any string), argument n is a number between 1 and 36.2 (regardless of machine type)). Any given component makes some assumptions about what it can handle, and if you can easily declare those assumptions and validate against them, then the component is easy to read, easy to understand, easy to use, safe against user errors, and easy to implement (as you don't have to write any code to handle conditions not allowed by the declarations).
The beauty of Python is that semantic typing can be implemented very naturally within the language itself, which is crucial because it has to be extensible to handle any given domain's objects if it is to be of use. There are a variety of options, but my own package Param (https://ioam.github.io/param https://ioam.github.io/param) provides support for semantic typing, and if you look at some of the examples there you should be able to get a feeling for how you can have the best of all worlds in Python: freedom where you can handle it, constraints where you want them, and everyone being up front about what they are doing in a way that makes big teams able to work freely with whatever other people come up with.
- willtim 9y agoTypes in any modern statically-typed language should go well beyond simple "machine types" and also capture semantics. If one is representing everything with Strings and Doubles, then the type system is not being fully exploited. Refinement types can statically capture range checks and other similar assertions. There is a project to add them to Haskell (Liquid Haskell) which is already quite useable. Of course, static types are machine checked prior to running the program. None of your Python "types" exist until runtime, by which point it is too late. The program crashing due to an assertion failed error is logically still a crash. We will have to agree to disagree on the roll of static types when building large systems. I have also spent over a decade working on very large codebases. The more "dynamic" a system was (late binding, reflection, string-based lookup, variants), the harder it was to statically reason about the code and avoid introducing errors. In a fast-paced business where hundreds of people are changing the same codebase daily, I do not understand how one could stay sane without static types. Python was never designed with such use-cases in mind.
- jbednar 9y agoSounds like we'll have to agree to disagree about a lot of things! :-) Having types specified statically is a recipe for generating untold many different versions of the same thing, plus untold bits of glue between them, which leads to huge codebases that need hundreds of people to work on them. Python was designed to avoid huge codebases and let smaller numbers of programmers be more efficient. (Of course, so was Haskell, but that's mostly a straw man, given how few large-scale users of Haskell there are in industry...)
- tome 9y ago> Having types specified statically is a recipe for generating untold many different versions of the same thing, plus untold bits of glue between them Polymorphism is for addressing exactly that objection.
- jbednar 9y agoAnd Python's duck typing is for addressing the needless constraints that statically typed polymorphism introduces. Discussions like this can go on forever! :-)