ClipboardImage.png
I realised that current programming languages have insufficient abstractions. They usually just give you classes and functions, but they usually don't give a primitive to describe free-standing execution threads (services), and neither do they allow you to define a single service interface that then generates both a sender and receiver end which would allow strong decoupling and easy connecting. Currently, you would need to write an abstract sender and an abstract receiver class to achieve such decoupling, which is needed for simulating parts of your program for tests (e.g. simulating user inputs, or simulating backend behaviour).
I came up with three new features I want to add to my language: protocols, a glue operator, and a MASK(Type) operator. A protocol is a collection of functions/commands, and multiple protocols can be combined into a composite protocol. They aren't instantiable, but if you have a protocol P, then P SERVER is an abstract type that implements the receiving end of the commands, P CLIENT is an abstract type that covers the client-side functionality.
The glue operator can be used to instantiate a concrete type from two or more abstract types, under the condition that all abstract functions in every type also exist as non-abstract functions on one of the other types. For example, if you have a decoupled abstract type pair as in pic related, then gluing them together via ControlPanel | VendingMachine can form a new type that automatically links the calls to handle_input() and display(). This kind of very easy type construction will allow you to write mocked control panels or mocked vending machines and simply gluing them together to a single type, and there is no runtime penalty for the abstractions (no virtual calls required).
Then, using MASK(ControlPanel | VendingMachine), you get an abstract (not instiantiable) type that can hold any type implementing the same functions as ControlPanel | VendingMachine. This allows switching out parts of the code without any effort. Note that this is a compile time substitution, it has to be injected statically. For that, I added a rough concept that allows setting context variables and types for instantiated types or called functions. They work similar to setting environment variables in a shell invocation:
ENV_VAR=VALUE my_program --args...
Now you can do something like this:
[]{MASK(ControlPanel | VendingMachine) = MyVendingMachine | MyControlPanel} shop: Shop;
This injects a type binding specified inside the {...} into the variable shop, replacing any matching instance of the left-hand type with the associated right-hand type at compile time. You now can not only have abstractions and split interfaces that are no hassle to connect, you can also simply reuse your production code and swap out global variables, certain types, or even override any function you want upon instantiation to inject the desired behaviour. You can turn log calls into no-ops statically, you can mock network communications, override syscalls, etc. You can also simply create a wrapped version of a function that measures its execution duration, and then replace the original with the wrapped one for profiling.
All of this will take quite a while to implement though.