The Boomla language was designed for the Boomla platform. It’s primary focus is on simplifying web application development.
Let’s see a hello-world example to get a feel of the language.
Boomla supports storing any value on disk, without manual serialization / deserialization.
To allow addressing those values, we have a special type constructor file{}
which stores a set of fields (like struct{}
) and also a file ID when
stored on disk, which is used for transparently referencing those values.
A file{}
is still a value, just like a number is a value.
You can assign it to a variable and pass it around.
When storing a file{}
, you get back a reference to the stored value, which
has type $file{}
. Any type in the form $T
is a reference to a stored value
of type T
. To rephrase, $
stands for Stored.
In the above example, we don’t specify where the value should be stored, so a new temporary filesystem is created with the root file being the one we just stored. (That’s the best we can do in this playground.)
Reading and writing stored values has the same syntax as reading and writing non-stored values.
Boomla has native support for generating HTML5 views.
It also has native Turbo CSS integration, which is the design language of Boomla.
Unlike in JSX, you can embed code blocks inside tags.
In the docs, you will find it under Xml expressions.
We really like how the explicit error handling of Go works. It is extremely verbose though. Alternatives like options are equally verbose.
Instead, Boomla splits a function’s results into success results and optionally
a failure result. Use return
for success results, fail
for failure results.
// Success result only fn sum(a, b int) (int) // Success result, failure result fn sum(a, b int) (int) (error) // Multiple success results, failure result fn swap(a, b int) (int, int) (error)
At call sites, passing on failure result is either implicit or explicit.
Implicit error handling means that if a function call fails, the failure result is automatically passed on. This requires that the failure result of the call is assignable to the failure result of the function in context.
Explicit error handling requires that you either capture the failure result and
handle it yourself, or use a ?
after the call, which is a shorthand
for passing on the failure result explicitely. As with implicit error handling,
the failure result of the call must be assignable to the failure result of the
function in context.
To turn on explicit error handling within a function, place a ?
after the function signature, before the function body.
Semantics roughly means behaviour. There is value semantics and reference semantics.
Reference semantics is about having multiple references to the same underlying value. Let’s just call them pointers. Explicit pointers are hard to understand so some high level languages like JavaScript hide them by having certain types that behave as pointers (Objects, Arrays), while the rest behaves as values.
Unfortunately, pointers are fundamentally flawed. For one, they are a common source of bugs. That’s bad. But there is worse.
Pointers encourage tight coupling in your programs.
Pointers encourage tight architectural coupling via parent pointers. You create a Comment type and suddenly you can’t have a Comment object without having it belong to a Post that belongs to a Category that belongs to a Website.
Attaching the hidden complexity of parent objects makes it really hard to fully understand and reason about a program.
Value semantics is about value independence. That changing one variable won’t change another. To rephrase, two variables can’t have shared mutable state.
Avoiding shared mutable state can be done in two ways. You can disallow the shared part or the mutable part. Functional programming languages go after the mutable part by completely disallowing mutations, using copy-on-write data structures. Boomla disallows sharing instead, which has better performance and ergonomics.
Independent values a
and b
:
Inout argument ~a
allows mutating the original variable.
Note:
File oriented programming is a concept where files are used to structure your codebase.
Note: these are not the files you know. The entire concept of what a file is needs to be redefined.
Imagine you are building a one-off wrapper component for a web application. It will render a border around whatever it displays. Let’s just say it will display “hello world”.
In a typical web framework, you would create some kind of a Wrapper component, then wrap the child component inside it. Like so:
<Wrapper>hello world</Wrapper>
There are several problems with this:
With the file-oriented approach of Boomla:
The file-oriented approach uses file-level encapsulation to simplify your codebase.
The wrapper file could be a Boomla source file and contain:
var child file interface{
Render() (live.View) (error)
}
fn Render() (live.View) (error) {
<div turbo="t1 b-4-red p-16">
{child.Render()}
</div>
}
The child file could be a markdown source file and contain:
hello world
Notice: