Hi folks! wanted to share something I’ve been working on these last few weeks.
Riot is an actor-model multi-core scheduler for OCaml 5. It brings Erlang-style concurrency to the language, where lightweight processes communicate via typed message-passing.
Why Actors?
It raises the abstraction of multi-core so you don’t have to think about threads and low-level multithreading primitives, and instead can focus on structuring your application in terms of hierarchies of processes that do some work, and how they communicate with each other.
Erlang (and by extension Elixir) has been doing this for impure functional programming for decades now and in my experience it is an incredibly productive way of working. So I’m hoping to establish the foundation for doing this kind of application development but with types.
NB: This is the dual of the Caramel project, which is an alternative backend for OCaml that compiles to Core Erlang, thus letting you run some OCaml on the Erlang VM.
Getting Started
You can get started with: opam install riot
Hello world
The simplest Riot program you can write is this:
Riot.run @@ fun () -> print_endline "hello world"
But there’s a small caveat here: this program doesn’t terminate.
Riot, like the Erlang VM, is designed for long-running applications. Despite this, it starts up reasonably fast (~4ms on a 10-core M1 Max). To terminate the runtime we must call Riot.shutdown ()
.
The smallest Riot program is then: Riot.(run shutdown)
Hello world, with messages!
The next smallest Riot program you can write is a hello-world that uses Processes and Messages:
open Riot
type Message.t += Hello_world
let () =
Riot.run @@ fun () ->
Runtime.Log.set_log_level (Some Info);
let pid =
spawn (fun () ->
match receive () with
| Hello_world ->
Runtime.Log.info (fun f -> f "hello world from %a!" Pid.pp (self ())))
in
send pid Hello_world
Hello world, with applications
Once you want to start building applications with supervision trees, to nicely decouple the subsystems of your app, you can use the Application
interface, and Riot.start ~apps
:
module My_app = struct
let start () =
let pid = spawn_link (fun () -> (* ... do work! ... *)) in
Ok pid
end
Riot.start ~apps:[
(module Riot.Logger); (* we want to start the logger app first *)
(module Riot.Telemetry); (* we then start the telemetry app *)
(module My_db); (* then start the database server *)
(module My_app); (* finally we start our app *)
] ()
Next Steps
If you’re interested in trying Riot, I’ve started working on a tutorial that starts from zero and works its way up. It’s not complete yet but it should get you up and running and building interesting programs!
I’d love any feedback y’all have about this especially interested in what would you like to build with it or what libraries you’d like to use in this space.
Also happy to answer any questions
/ Leandro