Modularity
—
There's a concept has been haunting me over the past few years…
It's not a new concept, in fact it's something very basic. The concept of modularity is really commonplace in regular life, but I feel is somehow foreign in the world of software.
For instance, you don't soldier a toaster directly to your wall to get power, most houses have a much safer electrical interface.
But with code we hardwire things all the time, to give some examples:
open('file.txt').write('blah')
print('whatever')
requests.post('https://ohno.com')
These all are effectively soldiered to the wall. To demonstrate why:
- What if you didn't want to write to a file, but you wanted to write to s3?
- What if you didn't want to print to stdout, you wanted to print to a log file instead?
- What if you wanted to cache the network call?
In each scenario you will have to pull our the soldiering iron, and edit the code because it's hardwired. Writing code this way traps any useful logic into it's context, and requring careful "rewiring" to free it again.
Modular code on the other hand does not demand this on it's operator, but how it possible?
You might think I'm going to talk about pure functional programming, but now, I think that's still missing the point, it's not about elliminating or obfuscating side effects, but managing them.
As I discussed briefly in my last post on platforms, there exist a concept called "Managed Effects". Sadly in my research I've found this to be mostly lost in the mire of theory and I haven't seen much in the sphere of pragmatic programming.
I want to change that, so I'm building a interpreter for a language now, which is practically featureless besides the addition of the platform concept. The syntax will be lua inspired, and I'll aim for otherwise quirkless, simple, boring semantics.
I won't get into it in too much detail now, you can follow along with my blog as it progresses, but I'll give a sneak peak.
Foo = component
print = require("print")
on greeting
print("hello world!")
end
end
print = require("print")
myfoo = Foo { print }
on init
myfoo!greeting
end
Here we define Foo as a child component, that requires "print". It also provides the handler "greeting" which uses print to say hello world.
The main scope (implicitly a parent component) requires print from the parent, and passes it to be used as is by an instance of Foo, myfoo.
We then handle init by simply invoking greeting on foo.
This is a contrived example, but illustrates how you can write components and subcomponents that compose instead of hardwire.
Maybe it looks a little object oriented, that's probably because it is, but semantically, it diverges quite a bit from a typical OO language.