The issue isn't getting rid of type checking but moving type checking out of the way and using it where it is needed.
Layered type checking.
"Need to know".
The transport layer moves bytes. It should not apply type checking for anything beyond that. It should not need to know what's inside those packets of bytes.
Each layer should only type check enough to do it's job, but not bother to infer anything more detailed.
That's mostly opposite to what our programming languages do now. They type check all the way to the bottom. And, instead of treating type checking like a separate tool, they spray type checking into our languages and make things harder to understand because there's too much detail all over the place.
We could, say, write Prolog rules on the side that describe what the entities are, for anyone who cares to know, and, allow the Prolog engine to do the actual type checking (which is essentially what type checkers are anyway).
I want Software LEGO®.
I can't have Software LEGO® because our API's are too cluttered with detail.
My experience is that I never needed type checking when my programs were only 5 lines long. I re-acquire that same experience when I get to create black boxes (Software LEGO® blocks). There's so little going on inside a little black box that I can grok it all and I don't need fancy tools to help me out.
The trick is to keep that sense of smallness all the way through the program.
How?
Structure the architecture like a tree (not a graph) of little black boxes instead of like an infinite canvas of functions.
Don’t allow little black boxes to call functions in other little black boxes. They can only send mevents (event-driven, asynchronous message passing, pure dataflow with no control flow implications) through well-defined, explicit ports.
“Fire and forget” instead of “fire and wait for a return value”.
Allow fan-out - one little black box can send a mevent out of one of its output ports and that mevent gets copied to multiple receivers. (Or enforce something like copy-on-write, if low-level efficiency is of concern).
Don’t allow any little black box to distribute its own outputs to other little black boxes directly. Leave this task for their parent. The parent is like a dispatcher or router.
Don’t hard-wire types into the wiring. Outputs sent by a little black box must match up with what the receiver(s) expect. Or else it is deemed to be a “bug”. If you want to be anal, use a dynamic type check. On the other hand, if the innards of a black box are simple enough to understand, the programmer/architect can “see” that the connections are reasonable and doesn’t need fancy tooling as a reminder.
With implicit types
This is the top-level layer of a working compiler.
With Explicit Types
Transitioning
Can you do this with what we’ve already got?
Yes.
Closures, queue classes, garbage collection for Parts (aka black boxes).
XML parsing or PEG parsing for diagrams.
See Also
Email: ptcomputingsimplicity@gmail.com
Substack: paultarvydas.substack.com
Videos: https://www.youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/65YZUh6Jpq
Leanpub: [WIP] https://leanpub.com/u/paul-tarvydas
Twitter: @paul_tarvydas
Bluesky: @paultarvydas.bsky.social
Mastodon: @paultarvydas
(earlier) Blog: guitarvydas.github.io
References: https://guitarvydas.github.io/2024/01/06/References.html