:candy: a functional language for building type-safe, scalable, and maintainable applications
1. == 1.0
)Math
moduleCaramel is starting to take shape, and we'll now refer to it as a language rather than an OCaml backend to make it a little easier to talk about it.
After all, it isn't really 100% OCaml but a strict subset of it. It doesn't ship the same standard library or tools.
We also have a new website at: caramel.run
We've updated our Code of Conduct to the Contributor Covenant v2.0.
We've opened the Github Discussions -- drop by and say hi or grab a Discord invite link! 👋
I've started work on the Caramel Manual where we have an installation guide, an introduction, a small syntax cheatsheet, some examples, and a small guide on writing Caramel code that talks to existing Erlang code.
This is a big work in progress, so if you'd like to help, please reach out!
In the future I'm planning to write guides for how to use Caramel with existing build systems like Rebar3 or Mix, and include a reference guide to the standard library.
Caramel now ships a formatter that you can run to make sure your code is always stylish and you have no more bikesheds. It supports ZERO configuration, so what you see is what you get.
This is currently just a wrapper around ocamlformat
, and all the kudos go to
the amazing team putting that together.
You can use it by calling caramel fmt *.ml
and it should work on both .ml
and .mli
sources.
It only does in-place formatting.
The compiler has dropped support for several targets, including Core Erlang to Native, and the default OCaml compilation modes.
It will from now on focus only on taking Caramel sources into runnable Erlang and Core Erlang code. Over time we will move from Erlang to only Core Erlang support, to make it easier to reuse Caramel code from Erlang, Elixir, Gleam, and Hamler.
The additions to the compiler are:
.cmo
)&&
, ||
, and >=
The standard library has been simplified for contributing to it, and you can now find it at the top of the repository.
Changes:
^
operator: "yay" ^ "!!!"
Binary.split/3
to split binary stringsErlang.floor/1
to round down floats to integersErlang.list_to_float/1
to parse strings to floatsErlang.float_to_list/1
to turn floats into stringsErlang.list_to_integer/1
to parse strings to integersErlang.integer_to_list/1
to turn integers into stringsLists.foldl/foldr
signature has been fixederlang: the Erlang library included, with a lexer/parser/AST/printer for Standard Erlang, is now completely split from the Caramel code and will be published to opam shortly.
caramelc: will return exit code 0 if everything went well. Otherwise expect a non-zero status!
stdlib: remove dependency on the Erlang AST printer for parts of the
runtime (like the recv
function), and instead include the relevant
.erl
sources as part of the packed stdlib.
docs: better contribution notes, documenting the release flow and saying a word about the rationale behind it. I've also put together a small website for Caramel here: https://caramel.abstractmachines.dev
examples: the echo tcp server has been refactored to make it harder to
accidentally override the gen_tcp
module that is shipped with Erlang.
We'll have to figure out a nice way to prevent these things from
happening, which may just mean using all the modules on the Stdlib to
avoid redefinition.
ci: several changes to CI to ensure we can release the erlang
library to opam.
Not much, just a few internal refactors that aren't very interesting.
Next release tag will include a better changelog.
This is the first actual release of Caramel that I'm making available 🎉
It includes 2 things:
caramelc
, the Caramel compilererlang
, an OCaml library for handling Erlang code, including an AST, lexer, parser, and printerCaramel can compile a large portion of the OCaml language, in particular most of the immutable functional OCaml, and into idiomatic Erlang code. Whenever possible it will look quite decent, and some times it will have pretty terrible formatting.
It runs as a single stand-alone binary with no dependencies (other than glibc or musl), and it should be blazing fast. Here's some benchmark results from the examples/ocaml_to_erlang.t
folder:
The compiler also includes an additional target that may be of interest to those who want to dig into the compilation process: caramelc parse
. This command will read Erlang and OCaml sources, and dump different ASTs of them. It was very useful to have around for testing purposes as well.
erlang
library for OCamlThe Erlang library currently includes support for the vast majority of the Standard Erlang language, according to the grammar shipped with Erlang/OTP as of version 24.
It includes a series of modules to make it easy to construct Erlang programs from OCaml, as well as deconstruct them and operate on them.
A pretty printer is also included that right now is used within the Caramel compiler to generate the Erlang sources. It unfortunately still has some tiny remnants of Caramel logic in it, but they should be cleaned up in the near future and should not get in the way of more general usage of this library.
compiler: preliminary support for guards is added in this release.
They are "safe" as long as you don't redefine any of the expected
functions. There is no good way at the moment to prevent this from
happening, but we could achieve it by essentially forbidding the use
of the module name Erlang
and requiring all guards to be fully
qualified names.
This still leaves us with the issue of compounded guard expressions.
At the end of the day, we want a function is_valid_guard : expression -> bool
that can traverse an arbitrary expression and tell us if it
is or is not a valid guard based on the specification found at the
Erlang docs.
caramelc: the parse
subcommand now can take a --lang
and --tree
parameters to print out the parse and typed trees for OCaml, as well
as the parse tree of Erlang, and the parse tree of the result of
compiling OCaml to Erlang.
This is particularly useful for testing and understanding the AST translation, and will likely be used later on to see if the compile -> typecheck -> compile cycle yields the same inputs, and thus is an isomorphism.
{ my_record with field = value }
My_record#{ field := value }
let f () =
let g () = 1 in
g ()
f() ->
G = fun () -> 1 end,
G().
compiler(#12): the compiler will now let you know when you're redefining a function on the OCaml side, which is not allowed on the Erlang side and stop compilation.
compiler(#16): shadowing bindings with let are (for) now unsupported
on the OCaml side, which makes translation runtime safe. We won't see
any more X = X + 1
on the Erlang side.
compiler(#15): to help with #16, priming of variables is now supported
and translated to valid Erlang. We can write x' = x + 1
and it will
translate to X_prime = X + 1
.
compiler(#13): recursive let bindings within a function definition are now not supported since they don't have a direct Erlang equivalent and require runtime overhead.
error messages have been created for all of the above
Better releases
compiler: automatic function reference arities! As see in #10
compiler: We're ignoring all fresh type variables when translating to Dialyzer specs for now. More work will be done in #20
caramelc: caramelc compile
now supports multiple --target
flags,
so you can compile both archives and Erlang sources at once.
caramelc: standard library will now by default be in the respective installation directory (respecting dune install conventions)
stdlib: Process.spawn/1
has been renamed to Process.make/1
until
we have support for module attributes (see #21)
stdlib: Dropped top-level namespacing until we figure out how it can work best with .merlin
ci: several changes to release flow, including a nicer folder structure in the tarball
ci: entire codebase is instrumentabled by bisect_ppx now to start gathering coverage reports
erlang: removed an unused helper