Incremental Strong Typing

You're not coupling things up where they don't need to be coupled, are you?

Incremental Strong Typing

Incremental Strong Typing, also known as Isolated Strong Typing, is a process and practice of programming that leverages the fact that scope and types are the same thing. Manage one by managing the other. The program should never fail or exist in an invalid state. Managing complexity and ease-of-coding and maintenance means doing this well and adding complexity and constraints only in the places where it is absolutely required.

Well-written functional systems become impossible or difficult to deal with by providing inadequate cues to current and future developers in terms of mapping the code to the problem. Well-written object-oriented systems become impossible or difficult to deal with by providing too many well-meaning yet false cues to current and future developers in terms of using human language to map the work to the code.

If we limit our code to doing only one important business function, we limit the number of human words required to reason about it. This also limits the number of coding symbols required to solve the problem, addressing the problems of both well-written object and functional applications.

The key issues then naturally become: what is an important business function and what does the code need to do to provide it? Since our premise is that scoping and types are the same thing, addressing one issue simultaneously addresses the other.

Put differently, whatever your app, there are three questions you have to answer. Each question results in different kinds of code, and failures in executing those three different kinds of code results in three different kinds of work.

We run into problems by ignoring the differences in what kinds of code are needed to answer these three questions and then mixing up our answers all into one spot later in the code

Want to learn functional programming? We'll do it by concentrating on the innermost part of this diagram. The common complaint is that the examples are nice but don't seem to help any with solving real-world problems. Want to learn OO? We'll do it by concentrating on the relationship between the inner two boxes. The common complaint is that teaching is light on writing good functions and the size of the code quickly zooms out of control. Having problems with nothing running or things running badly? That's the operations folks. They don't have to know anything about what's inside the boxes. That's all software. The common complaint is that trying to separate operations from development was always an idiotic idea, we just got away with it for a while because the overall complexity hadn't grown out-of-control yet.

Examples

I would like to sort these numbers

sort -n numbers.txt
In BASH, here you go. No code needed.
> [14;65;63;1;55;33;19;188;44;7]|>List.sort;;
val it : int list = [1; 7; 14; 19; 33; 44; 55; 63; 65; 188]
In a functional language, here you go. This is way too much for the problem provided.

I could provide an OO example, but I've already gone too far out into the weeds with the F# example. How do I know that? I know that because the three questions I have in the diagram above are completely-answered by the BASH statement. Does it do one useful thing? Yes. Can I bidirectionally translate any business data it need to do that thing into strong types to make sure I can't fail? Sure. The business requirements are just to sort. I assumed we were sorting numbers, but if that's not true, I'll just take the "-n" out. No new code needed. Can I R/W with the underlying operating system? Sure ... more importantly, if I can't, there no new business functionality needed (as opposed to, say, an application that adjusts a credit card balance that fails on updating the available credit. In that case a system error will almost automatically involve some kind of business discussion, and how to fail gracefully becomes a huge deal)

I would like to list my mp3s by artist or album

On a lark, I did this yesterday to help sort my own music files. Turned out to be fun.

apt install python3-mutagen
mid3v2 *.mp3 | grep TPE1 | sed s/TPE1=//g | sort
mid3v2 *.mp3 | grep TPE1 | sed s/TALB=//g | sort
There's one piece of business data here, whether it's artist or album you want to sort by. There should be one piece of code. Scope and types are the same.

Now, imagine handling this same problem in, say, C#. I would still be screwing around with the IDE. (And more importantly, I would have to continue screwing around with all of that stuff every time I wanted to add or change anything. I'd end up using/creating classes for directories, files, mp3s, IDv3 tags, and so on. It's inappropriate complexity for the nature of the business problem)

I would like ...

(Sorry, I can't think of a third example here. Create your own.)

This essay is both a process and practices discussion. Binding is a practice. Scoping is a process. From the outside, each piece of the system is late-bound and dynamic-typed. The inside is tightly-bound and static-typed. The process of moving from no types to strong types controls the number of functions/apps/microservices needed for the overall solution.

Objections

  1. There is no such thing as "strong" typing. Yes, that is correct. There's a huge discussion here around typing if you'd like to have it, but the point is to add type constraints only when forced to and only in the places where they are needed.
  2. But my total applications does a hell of a lot more than just this one thing. Sure. So does mine. So we split them up into important business things. Then we have business discussions where they belong and system discussions where they belong, instead of mixing them all together.
  3. Aren't you killing DRY by writing lots of code that does the same thing? Are we? When I ask whether or not a customer account is valid for purposes of sorting on last name, am I really asking whether or not a customer account is valid for a line-of-credit increase? There are two cases here. One, the same word, "valid" is used but it means different things depending on what the business wants. In that case it's really a different thing. Two, that we're actually doing the same thing. In that case it's a generic function. In either case you're not duplicating the code.
  4. If we're splitting everything up so tiny, won't that create a disastrous mess? It will if you don't manage it, just like everything else will create a mess if you don't manage it. As programmers, we have control of everything. We can, for instance, create strong types to join applications or functions. In fact, that's what the DDD guys do in F#: strongly type the connections between functions so that you don't have an un-typed mess of connections everywhere. Whether all of that compiles into one function, app, microservice, or whatnot is a deployment decision, not a coding one. Your code and deployment isn't that tightly-coupled, is it?*
  5. If we're going so small, why use any typing at all, then? Just go dynamic with everything. See #1. I think dynamic typing is awesome when you don't know (nor care) what you have. Once I start caring, which is very rare, I don't want to forget what's important about this variable name.

Any objections I've missed? Let me know and I'll happily address them.

The practice of selectively-using tightly-coupled static types to prevent important parts of the program from being in error is called Incremental Strong Typing. The process, as in the notepad list above, is called Kangaroo Programming. (No kangaroos were harmed in the writing of this essay)

Conclusion

Hopefully these examples seem trivial to you. That's the entire point. Scope and type usage are the same thing. Whether it's signing up for a new service or having your phone recommend the best restaurant to use, people want simple things from their applications. When we fail to manage scope and types, we create unnecessary confusion and complexity. Code Cognitive Load increases without bound. We end up with vast complicated systems that might possibly do anything. They instead do nothing except create more vastness and complications.

(These concepts are further discussed in Info-Ops 2, part two of the three-part series on managing information while creating technical solutions)

*There's a really cool thing called a "supercompiler" that you should be hearing a lot more about in the future.

As with many of these technical topics, you can find them covered from different angles. Here's the excellent "Parse, Don't Validate"