I'll try to answer the following questions:
1. What is Caramel and why should I care?
2. What can we build with it?
3. How do I get involved?
is...
as an OCaml to Erlang compiler
Lets you write OCaml code
and generates type-safe Erlang code
(* file: hello.ml *)
let hello name = Io.format "Hello, ~p!" [name]% Source code generated by Caramel.
-module(hello).
-export([hello/1]).
hello(Name) -> io:format(<<"Hello, ~p!">>, [Name|[]]).that you can just run.
as an OCaml to Erlang compiler
It has some limitations:
let add () =
let i = ref 0 in
i := !i + 1;
!iclass stack = object
val mutable v = [0; 2]
method pop = (* ... *)
method push hd = (* ... *)
endNo Mutable State
No Classes
But we are used to this on the BEAM 😉
as an OCaml to Erlang compiler
And it lets you use the OCaml type-system to rule out things that should not happen.
let add x y = x + y
add "goodbye" 2020
^^^^^^^^^
This expression has type string but an expression was expected of type
int#1 - All functions always receive good arguments
as an OCaml to Erlang compiler
let user = User.make name in
User.promote user
^^^^
Error: This expression has type regular user
but an expression was expected of type promotable user
Type regular is not compatible with type promotable
#2 - Types can help us prevent bugs
as an OCaml to Erlang compiler
(** A user in our system. *)
type 'kind user = {
(** the name of the user *)
name: string
}
let user = User.make "Joe"
user
- : regular user = {name = "Joe"}#3 - Types can help explain our programs
% Source code generated with Caramel.
-module(user).
% A user in our system.
-type user(_Kind) :: #{
% the name of the user
name => binary()
}.as an OCaml to Erlang compiler
type 'kind user = {
name: string;
(** new field added! *)
age: int
}
let make name = { name }
^^^^^^^^
Error: Some record fields are undefined: age#3 - Types can help us refactor our programs
as an OCaml to Erlang compiler
Extends the OCaml compiler and provides a new compilation target:
.ml / .mli
sources
.erl
sources
world class type system with >25 years of industrial use
and research
as an OCaml to Erlang compiler
It inherits a lot from OCaml:
Built for speed and scale: companies like Jane Street compile millions of lines of OCaml code every day.
Sound and Correct: if the compiler says you're good to go, you're guaranteed no runtime errors.
Pragmatic: the type-system has evolved to be intuitive and usable in the industry.
as a type-checker for Erlang
You give it Erlang code, and it finds bugs:
-module(typed_send).
-export([start/0]).
loop(S) ->
receive
{replace, X} -> loop(X);
print -> print_int(S), loop(S)
end.
spawn_int(S) -> erlang:spawn(fun () -> loop(S) end).
start() -> spawn_int(0) ! {replace, <<"wrong value">>}.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type [> `replace of string ]
but an expression was expected of type [< `print | `replace of int ]
Types for tag `replace are incompatibleIt reuses the OCaml type-checker, by translating the Erlang sources into OCaml.
.erl
sources
OK
Type errors!
as a type-checker for Erlang
.ml
as a type-checker for Erlang
It has some limitations:
run(Mod, Fun) ->
Mod:Fun([1,2,3]).receive
X when is_integer(X) -> ok;
_ -> err
endNo Dynamic Dispatch
No Untagged Case/Receive
We sometimes do this on the BEAM 🙈
What can we build with it?
Typed interfaces to existing code:
* OTP, Phoenix, Cowboy, Ecto
Business logic that needs to be airtight
Use it as building block for code generation:
* Generate JSON validation code
How do I get involved?
Caramel as an Ecosystem Bridging Project
+
=
as an Ecosystem Bridging Project
Let all existing OCaml code run on the BEAM
Make it easy to reuse existing infrastructure
Bring more static safety to the BEAM
* direct compilation
* NIF generation
* typed interface generation
* type check and automated fixes to existing Erlang/Elixir code
* parsers for Erlang and Core Erlang
* tools that work at the Core Erlang level
* general libraries to build static analysis tools
as an Ecosystem Bridging Project
So far what we have built is:
Backend to the OCaml compiler
Infrastructure for building, testing, and publishing
Proof of concept for typed interface to Erlang stdlib
Library for working with Erlang sources from OCaml, which includes a Parser, an AST, and Printer for Standard Erlang
tree-sitter grammar for Standard Erlang (WIP)
Translations from the Erlang AST to the OCaml Parsetree that enables the Erlang type-checking
hex and rebar3 plugins
bindings to tree-sitter library to use new grammars for Erlang/Elixir
as an Ecosystem Bridging Project
But there's plenty to be done!
Verify compilation is sound by fuzzing the compiler
Better integration with build systems to leverage incremental type-checking and compilation
Wider surface area for typed interface to Stdlib and OTP
Improve OCaml libraries to manipulate CST and ASTs for Core Erlang and potentially Elixir as well
Finish tree-sitter grammar for Standard Erlang
Generate typed interfaces for any Elixir / Erlang library
NIF generation for OCaml code that can't be trivially ran on the BEAM
tree-sitter grammar for Elixir / Core Erlang
Build static analysis and refactoring tools that rely on types
Support ReasonML and ReScript syntax
as an Ecosystem Bridging Project
8 individual sponsors
1 company
7 fabulous contributors
(one without a picture!)