https://github.com/matthewelse/sriracha
🌶️ ⚡️ hot reloading ⚡️ for OCaml
https://github.com/matthewelse/sriracha
hot-reload ocaml ocaml-library
Last synced: 9 months ago
JSON representation
🌶️ ⚡️ hot reloading ⚡️ for OCaml
- Host: GitHub
- URL: https://github.com/matthewelse/sriracha
- Owner: matthewelse
- License: mit
- Created: 2025-08-09T19:19:15.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2025-08-10T17:22:51.000Z (10 months ago)
- Last Synced: 2025-08-10T19:12:54.229Z (10 months ago)
- Topics: hot-reload, ocaml, ocaml-library
- Language: OCaml
- Homepage:
- Size: 39.1 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
🌶️ sriracha 🌶️
Type-safe ⚡️ hot sauce source reloading ⚡️ for OCaml 🐪
## Introduction
Sriracha is a library for type-safe hot-reloading of OCaml functions, inspired by
[subsecond](https://github.com/DioxusLabs/dioxus/tree/main/packages/subsecond).
## Usage
🌶️ `sriracha` 🌶️ consist of two parts: the loader, and the application.
In short, the loader is a lightweight application that dynamically loads your application,
starts it, and decides when to live-reload your application. The `sriracha` library provides
the glue between the two.
Both the loader and application should depend on the core Sriracha library. Your application
should (perhaps surprisingly) depend on your loader[^loader], `ppx_sriracha`, and `ppx_typerep_conv`.
[^loader]: This allows your loader to specify what type of main function it expects (`unit -> unit`,
`unit -> unit Deferred.t`, etc.).
Basic example of loaders for Lwt (+Dream) and Async are provided in `loader/`, but for more
complicated use-cases, you likely want to build your own loader.
See [hot_loader_async.ml](loader/async/hot_loader_async.ml) for a basic hot-loader built on top of async.
See [example.ml](example/basic/basic.ml) for an example application.
Running the example application
```bash
# run this in one terminal
$ dune build example/basic/basic.cmxs loader/async/bin/loader.exe --watch
# in another terminal
$ _build/default/loader/async/bin/loader.exe _build/default/example/basic/basic.cmxs
```
You can then make edits to `example.ml` (e.g. changing the text printed by `do_something`).
Notice how only changes in, or "downstream" of `do_something` affect the runtime behaviour.
Running the example dream application
```bash
# run this in one terminal
$ dune build example/dream/dream_example.cmxs loader/lwt/bin/loader.exe --watch
# in another terminal
$ _build/default/loader/lwt/bin/loader.exe _build/default/example/dream/dream_example.cmxs -L digestif.c -L dream
```
You can then make edits to `server.ml` (e.g. changing the text returned to the user).
## How it works
Sriracha makes use of the OCaml [`Dynlink`](https://ocaml.org/manual/5.3/api/Dynlink.html#top)
library. It builds a type-safe jump table from function name to function pointer, and redirects
calls to hot-reloadable functions via the jump table. This jump table is updated as the program
live reloads.
There are two parts to an application using Sriracha: the loader, and the application.
The loader is a very simple (approximately application agnostic) executable, which bundles
all of the application's dependencies (see notes below -- this is quite annoying), and is
responsible for (re-)loading the application, and launching it.
The application is just the application you want to live reload. For the most part, all you
need to do is to define an entry point for the loader to call, and to annotate reloadable
functions as `let%hot`.
It's likely that a more complete integration with this library will require e.g. your web
framework to be aware of hot reloading. In the future, we'll likely provide hooks to
receive updates about e.g. hot reloads happening to clean-up after old versions of your
code as necessary.
## Ideas
- [x] add a flag to `ppx_sriracha` to statically remove all of the hot-reloading points in
release builds.
- [ ] handle nested function calls correctly (e.g. detect a new function being called
within an old function.
- [ ] add some better notes about caveats/limitations of the library, e.g. the interaction
with global state, etc.
- [x] build a more complete example, e.g. a web server with Dream.
- [ ] thread safety?
Misc notes
Reasons this was hard to get right:
- Dependencies have to be linked into the loader, and it's quite easy for those dependencies
to accidentally be dead-code eliminated.
- User applications can't have both dynamic and static versions of a single application
(which would also solve the dependency problem), since you end up with module name clashes.
_Maybe we can do some name mangling?_