Incremental Strong Typing
You're not coupling things up where they don't need to be coupled, are you?
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.
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
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.
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.)
Objections
- 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.
- 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.
- 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.
- 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?*
- 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.
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.
*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"
Comments ()