As developers we contribute to multiple projects across multiple platforms.
Our job is to communicate our intent to the computer. Yet we waste countless hours learning equivalent syntaxes to communicate the exact same ideas, sometimes even on the same platform.
And some of these languages are so old they have to crowbar the syntax for new features in, resulting in code that is hard to read and write. And then the compiler keeps shouting at you. And then you die inside.
Optimizations vs Native
We fight endless flame wars online. The web world aggressively optimizes whilst classicists rage because native always wins.
We live in a world where the machines we use are quicker than ever but we settle for writing apps that are just good enough.
And yet still we hack away to bridge virtual machines in places they shouldn’t be, ignoring the voice in our heads that screams how dirty it is, or the user experience that suffers as a result.
The cost of forcing the semantics of our favourite source language onto a different platform is not free, and abstracting away the target platform in an effort to ‘ship it yesterday!’ is putting the business people before the end user.
These tools and frameworks are prime examples:
And they are popular for some good reasons:
- Making programming more accessible than ever
- Promoting the use of a single programming language
- Preserving the programmer’s intent
But even with that in mind we can do so much better whilst still delivering software on budget and on time.
This is what we get from abstracting away platforms and thinking they are equal:
Not to mention the debugging experience is soul destroying because of all the framework layers. The stacktrace is not deep enough to actually show me my code:
And despite branding to the contrary, ‘learn once, run anywhere’ is ‘write once, run anywhere’ in reality.
We can write code that is mindful of platform differences, whilst still leveraging the conceptual commonality between them:
This commonality is what makes semantic transpilation feel so intuitive. We focus only on the semantics of a target context.
How each one of those concepts is implemented. It’s constraints. It’s interface. It’s quirks.
Sempiler is a portmanteau of semantic transpiler. How it works is simple:
Take your favourite source language
- A language that is second nature to you
- A language that has an expressive syntax and a clear type system
- A language that is NOT a boundary to communicating intent
Take a target language or platform
- Understand the symbols and semantics of the target
- Do NOT waste effort learning the syntax of the target
Write source code with the target in mind
- Use declarative stub APIs that represent the symbols and functionality of the target (eg. pointers)
- Use Sempiler to validate source code statically against the semantics of the target, and emit native code for the target
- Do NOT abstract away the target platform
That is why we do not need to synthesize features in the source language, or use frameworks on the target platform.
That is why writing code against strongly-typed, purely symbolic APIs works.
For example, writing a
flexbox UI natively with no overheads:
When your code is sempiled those symbols will be valid in the emitted native code where they are implemented:
Sempiler preserves programmer intent so the emitted code feels the same as the source code, and is easy to debug.