Beta Release 0.1.13
Its been nearly 6 months since a release of Onyx — which is way too long! I've been putting off releasing this version until I felt there were enough changes to justify a new release. Now there are so many little changes made over these past months, it is due time to push them out to everyone!
Syntax Additions and Changes
Closures
Closures have existed in Onyx for several years, but the syntax was quite poor. Here is what they used to look like.
main :: () {
capture_me := "some string"
f := (x, [capture_me]) => {
printf("{}: {}", capture_me, x)
}
f(10) // "some string: 10"
}
This syntax was partially inspired by C++, but moving the capture group inside the parentheses. I have never felt happy with this syntax, and using it always feels clunky to type and a pain to look at.
The new syntax is not as "concise", but is much easier to read and is already used by other programming languages, most notably PHP.
Simply place the use
keyword after the close parentheses of the parameter list.
main :: () {
capture_me := "some string"
f := (x) use (capture_me) => { // still need '=>' because this is a quick procedure
printf("{}: {}", capture_me, x)
}
g := (x: i32) use (capture_me) -> void { // can omit '-> void'
printf("{}: {}", capture_me, x)
}
f(10) // "some string: 10"
g(10) // "some string: 10"
}
Note, in the future, explicitly listing captured values may not be necessary.
Unwrap operator
Onyx now has another post-fix unary operator called the unwrap operator, written x!
.
It is used by the Optional
and Result
types as an shorthand for x->unwrap()
.
main :: () {
iterator := Iterator.from(1 .. 5)
value := Iterator.next(iterator)! // Unwrap here
printf("value: {}\n", value) // Prints 'value: 1'
iterator
|> Iterator.map(x => x * 2)
|> Iterator.next()! // You can "pipe" into unwrap, just like try (?)
|> printf("another value: {}\n", _) // Prints 'another value: 4'
}
It can be overloaded using #operator!
.
MyType :: struct { /* ... */ }
#operator! :: (mt: MyType) -> i32 {
// ...
}
Custom commands
Custom commands allow the Onyx CLI to be extended to include new tooling for Onyx development. These tools used to be only installable globally, and they were not indexed, so you had to know which tools you had installed.
Now, these custom commands can be installed globally in the ONYX_PATH/tools
directory, or per project
in the ./.onyx
directory. These commands always come in the form of .wasm
files that
accept the remaining command-line arguments. In other words,
$ onyx tool-name arg1 arg2
is equivalent to
$ onyx run resolved/path/to/tool-name.wasm -- arg1 arg2
Custom commands now show up in onyx help
. They get their description by reading a custom section
in the WASM binary called onyx-command-description
. If you are creating a custom command and would
like to set this, you can use the #wasm_section
directive like so:
#wasm_section "onyx-command-description" "A concise description here."
New Core APIs
Onyx now has support for some common interchange formats and functionality used in web and server development.
Protobufs
There is now an official protobuf library for Onyx that supports serialization and deserialization.
There is also a protoc plugin that you can use to generate Onyx types from a .proto
file.
Basic JWT support
Onyx how has the beginnings of core.crypto.keys
, with basic support for decoding and encoding HS256 JWTs.
Once Onyx has support for other crypto functionality, namely RSA and EC, other token signatures can be implemented.
XML decoding
Onyx now supports decoding XML documents in core.encoding.xml
.
Encoding XML should just be done with formatted printing to a dyn_str
.
Other items of note
Changes to #load
directive
When loading more files into the current compilation, the #load
directive behaved in an
arguably strange way. It had a global search path, that you could append directories to using #load_path
or -I dir
. Unless you explicitly prefixed the file path with ./
, it would search
through all known paths to find a match. When it did not find a match, it would default to loading the file
from the current directory of the running "onyx" process.
This behavior is quite non-standard amongst programming languages, and so it is revised in this release.
Now, all #load
directives are relative to the path of the current file, unless
it is prefixed with a mapped folder name, followed by a colon, like so.
#load "foo" // Will load `./foo.onyx`
#load "core:foo" // Will load `$ONYX_PATH/core/foo.onyx`
By default there is only one mapped directory, called core
, that is set to ONYX_PATH/core
.
You can map your own directories using --map-dir NAME:DIR
on the command line.
Note, because of this change, the -I
command-line argument has been removed, and the #load_path
directive has been deprecated and no longer does anything. Mapped directories can only be set at the command-line.
Short-circuiting logic operators
Onyx has had the standard boolean operators &&
and ||
since its inception. But until this release, they did not behave how they do in
all other programming languages — they did not short-curcuit.
That was fixed in this release. These operators now short-circuit as you would expect them to.
Fixed-sized arrays passed by value
When trying to use fixed-sized arrays as "vector-like" types, there was
a lot of unexpected behavior when passing them to functions. This was because
fixed-sized arrays like [4] i32
were passed by reference. In other
words, a pointer to the first element was implicitly passed so any changes made to
the array would be reflected in the original array.
These semantics were copied from C and seemed logical for the time. But seeing how
Onyx has evolved in a different direction, this no longer makes sense, so it has
been changed — all fixed-sized arrays are now passed by value.
If you still want the pass by reference semantics, use a slice type instead ([] T
).
Referencing Git branches as dependencies
When specifying dependencies in onyx-pkg.kdl
, you can now target a particular
branch from a repository, instead of a version. This will re-clone the branch ever time you
run onyx pkg sync
, which is useful when testing a package you are developing.
dependencies {
some-package branch="feature-branch" git="https://github.com/user/repo"
}
Goals for 0.1.14
My primary focus for 0.1.14 will be to refactor the entire codebase of the compiler to separate out the CLI from the compiler internals. The compiler internals will be available as a standard dynamic library and header file, so they can be embedded into interesting places. The CLI will then be rewritten to use these compiler internals.
While in this process, I may extend the capabilities of compiler extensions with more hooks and program introspection. But, I have not yet designed what that system will look like, so that may be on hold until 0.1.15 or later.
Updating
To update to the newest version of Onyx simply use the same install script found on the homepage. It will automatically detect your previous install and will override it with the new version.
$ sh <(curl https://get.onyxlang.io -sSfL)
You can also run onyx self-upgrade
if you are on MacOS or Linux!
Happy programming!
Full Changelog
Additions
- Unwrap operator,
x!
. - Similar to try operator (
x?
), but panics if value is not present. - "Field update" syntax. A shorthand to change a field of a structure.
.{ ..old_value, field = new_value }
onyx watch
now works on MacOS.onyx run-watch
to automatically re-run the program on changes.#wasm_section
directive to add custom sections to the WASM binary.- Custom commands per project
- Installed in a
.onyx
directory onyx pkg build
can now run shell commands and multi-stage builds.Stalled
compiler hook to allow for code injection when the compiler stalls.Slice.map
Slice.map_inplace
Slice.fold1
Slice.get_opt
Iterator.from
iter.group_by
core.alloc.debug
core.os.args
core.crypto.hmac
core.crypto.keys
core.encoding.json.Value.as_entry_array
core.encoding.base64 { encode_url, decode_url }
core.encoding.xml
Changes
- Capture/closure syntax is now
(params) use (captures) ...
. (x: i32) use (variable) -> str { ... }
(x) use (variable) => { ... }
&&
and||
now short-circuit.- Fixed-sized arrays (
[N] T
) are now passed by value. - The size of the tag field for unions is now dependent on the number of variants.
- Parsing structs no longer sometimes needs
#type
. - Renamed
core.alloc.memdebug
tocore.alloc.memwatch
.
Bugfixes
- Many, many random bugs.