Second public release of this little actor machine and we have gone quite a bit from v0.0.6.
This is a ton of fun, and its starting to look like something that could be used for real. Binary outputs are small-ish, and they boot crazy fast too, so I can't wait to see what a properly optimized version of the emulator and bytecode encoding could do for it!
Changelog
lam
command line interface 🧑💻
The cli has seen some improvements, in particular the build
command, while still available, has been split into compile
and link
. The main reason for this is that I'd like to start playing around with lam
integration into the Crane build system and having a split compilation and linking stages means distributing these tasks can be done more easily.
In short:
compile
can be used to translate a number of *.beam files into *.lam files that include the LAM bytecode.link
can then be used to link LAM bytecode into an executable for a target, like Native, WASM, or Web.
You normally use it like this:
# this example assumes there's a my_module.erl that exports a `main/1` function
$ erlc *.erl
$ lam compile *.beam
$ lam link *.lam --output project.exe --target native --entrypoint my_module
$ ./project.exe
...
$ lam link *.lam --output project.wasm --target wasm --entrypoint my_module
$ wasmtime run ./project.exe
...
But of course you can use the shorthand build
too, that doesn't create intermediary outputs.
$ erlc *.erl
$ lam build *.beam -o project.exe -t native -e my_module
$ ./project.exe
...
Examples
I've included a few examples that I'm using to test out what this thing can do:
cat
a GNU cat clonegrep
a message-passing based "find exact match in files" tool that spins up one process per fileempty
, just an empty binary to measure boot timesimple_send
a small program that sends itself a message, prints it and exitshello_joe
, the mandatory hello worldfib
, a fibonacci number calculator (it goes really high! try it with like 100,000
Most examples have a ./build.sh
and a ./bench.sh
script that you can use to build them and test them for speed. But also most scripts rely on a globally installed lam
binary and the Rust toolchain (since ./build.sh
just does debug builds).
Emulator 🚀
New instructions allow us to:
Spawn
— spin up a process from a lambda and place itsPid
on a registerSend { message, process }
— send a message to a process (even ourselves!) and have it delivered before we continue executing- Receive messages with
PeekMessage { register }
, and if we like the message we can just move it into somewhere else and useRemoveMessage
to remove it from the queue. This allows us to support Selective Receives. Sleep
to put a process to sleeptTest(IsFunctionWithArity { fun, arity })
&mdash to check if an argument is a function ofarity
parametersShiftLocals { amount }
&mdash to shift local registers, dropping the firstamount
of themSplitListTail
andSplitListHead
to get only the tail or only the head of a listBadMatch
to essentially terminate a process when we could not pass someTest(..)
New emulator behavior will:
- Push new local registers onto the stack on
Call
instructions - Terminate a process on a
BadMatch
Additionally we've introduced a ProcessRegistry
to support the instructions above,
Runtime: Native 🏗️
The native runtime now includes a few more FFI calls, but I'm looking for ways that these could be either imported verbatim from the OTP sources, or reused across runtimes.
You can now call:
binary:list_to_bin/1
— to convert"erlang strings"
into<<"erlang binary strings">>
binary:split/3
— to split binary strings by one or more separatorslength/1
— to get the length of a list
And there's been some change in behaviors:
io:format/2
will now print out both the pattern and the arguments. It's still a little ugly but its better than no outputs!file:read_file/1
will not remove new lines and you'll have to usebinary:split/3
for that.
Roadmap 🗺️
Going forward I'm gonna see how hard it is to reuse the built-in functions that are now part of the runtime, and try to bring the WASM runtime to parity there. In particular I've had some trouble getting the file:read_file/1
to run on Wasmtime. If that goes well, I might as well start rewriting Crane in Erlang, and shipping it as a native binary.
And the ROADMAP doc should reflect a bigger picture of where this thing is right now.
Enjoy it and let me know what works, what breaks, and cool things you want to build with LAM! 🚀
/ Leandro