Vellum - mr-martian/quest-editor GitHub Wiki
Quest will have a bespoke extension language named Vellum, because we couldn't agree on any individual existing language and we thought we could improve on all of the options we considered.
Vellum is a general-purpose high-level compiled structured concatenative language leveraging LLVM for high performance. Unlike most languages, which are either procedual or functional, Vellum's operational model is defined by a concatenative programming model named Substrate. It will natively support linking to C++ and vice versa, so that extensions written in it will not be forced to use a C FFI API to communicate with Core (which will be written at least partially in C++) and with third-party libraries. But unlike C++, Vellum is not a systems programming language: there is no plan to create a freestanding version nor is it intended for writing low-level hardware interfaces in. Nor is it meant to be a scripting language, like JavaScript or Python, which many programs use for their extensions, though the ability to write short programs on the fly is a use case we are considering. Instead, it is specialized to be an applications programming language.
Some core design pillars for the design of Vellum are:
- The design of the language must not artificially restrict any use-cases. If a programmer so desires, they should be able to write a program that has nothing to do with Quest at all in Vellum.
- A language that makes you consider subtle issues is important if you want a maintainable project. Therefore, readability and long-term productivity have priority over short-term ease of writing short programs. Additionally, safety and clear semantics are more important than terse notation for bug-prone low-level operations, even if they are fairly common. However, balance and moderation is important; refer back to pillar #1.
- Despite the above, it is impossible to design a language which eliminates the possibility of writing bad and unreadable code. For that reason, useful features like operator overloading must not be sacrificed because of what they might be used for. Therefore, the language should be extensible to create DSLs which are more suited to individual applications. In support of this, and borrowing a design goal from C++, user-defined types should have the same level of support as builtin types in the language.
- The purpose of a static type system is to prove (a subset of) program correctness, not to bog the programmer down in pointless details. Typing should be automatic and user-friendly as much as possible, while still ensuring common bugs are impossible to make. With a well-designed static type system, "You almost never have to say what type something is, and you can change what type something is at runtime, just like for dynamic typing. You get duck typing and row polymorphism, just like dynamic typing. The only difference is that instead of a runtime type error when the duck typing fails, you get a compiler error, which also gives you more details."
- The same 'Rule of Least Power' described for program configuration should be applied to the language as a whole. Features including pervasive immutability-by-default, encapsulation, access control, and enforceable class invariants are useful precisely because they restrict what is possible for any given piece of the program to do. Limiting power makes programs more comprehensible and predictable.
- The macro/reflection system should not be a "shadow world", and should be capable of expressing any useful metaprogram. In principle, it should never be necessary to use an out-of-language code generator such as Yacc.
- Type classes and type constraints (Conor Hoekstra) both have unique advantages, as well as drawbacks. Bee believes that some advantages of type classes can be adapted to apply to type constraints as well, without bringing over their drawbacks as well.
- The language must natively support dynamic loading of modules in order to be an effective extension language, which is a rare feature in compiled languages (aside from in the context of derived class hierarchies, and not including ordinary dynamically-loaded libraries).
- C defined a very poorly-behaved and confusing system for integer arithmetic, especially as regards unsigned types. Implementing a new language allows us to take a different, better approach.
Major features (will) include:
- Predominantly-inferred static typing
- Pattern matching, including implicit pattern matching and compound type unwrapping
- Strong support for immutability
- Well-developed compile-time reflection and generative metaprogramming facilities
- Hygienic macros
- Builtin support for RAII
- Uniform Function Call Syntax (UFCS)
- Pipeline operators
- Modules and namespaces
- First-class functions
- Opt-in mandatory tail call optimization and named return value optimization
- Coroutines
- Error reporting with optional/result types, rather than exceptions
- Optional references replace nullable pointers
Vellum's syntax is not based on any individual language, but borrows ideas from C++, Rust, Zig, Nim, Haskell, Scala, and others. Variables and references are immutable by default and type annotations are almost all optional. Unlike most languages' generics, but like in Zig, Vellum templates use the same syntax as regular functions, instead of a separate "shadow syntax" with <>
that artificially separates certain kinds of information, preventing interoperability. Erlang's process model may be a good model for how Core should handle extension threads. If we decide to do that, we will probably incorporate some features from Erlang into Vellum as well, such as its message passing model.
You can view an annotated sample Vellum program written by Bee here.