devise a way of communicating between ludus and processes
This commit is contained in:
parent
b35657e698
commit
801e5bcc01
|
@ -774,4 +774,56 @@ See https://github.com/vitejs/vite/discussions/12826.
|
||||||
|
|
||||||
Web, in some ways, is even more straightforward.
|
Web, in some ways, is even more straightforward.
|
||||||
It produces an ESM that just works in the browser.
|
It produces an ESM that just works in the browser.
|
||||||
And
|
Put the rest of my thinking up in an issue on alea.
|
||||||
|
|
||||||
|
### Actor/Model
|
||||||
|
#### 2025-06-26
|
||||||
|
Okay, even though we're not fully hooked up with wasm yet, I've started working on the actor model.
|
||||||
|
I believe I have things broadly right.
|
||||||
|
The thing that I'm thinking about right now is how to connect ludus to the world and to processes.
|
||||||
|
It's not clear how to expose that plumbing.
|
||||||
|
|
||||||
|
The most obvious way to do this would be to make all the process stuff special forms.
|
||||||
|
|
||||||
|
But first, before I get there, this is what Elixir does.
|
||||||
|
|
||||||
|
* `receive` is a special form. It is strictly equivalent to `match`, but with the caveats that (a) if there are no messages to receive, the process yields, and (b) if there's no match for the first message, it matches against the second, and so on (keeping messages in the mailbox), and (c) if no messages match, the process yields as well.
|
||||||
|
|
||||||
|
Everything else is functions:
|
||||||
|
* `spawn/1` takes a function to execute
|
||||||
|
* `send/3` takes a pid and a message (the third argument is for options, which we won't have in Ludus), returning a pid
|
||||||
|
* `self/0` returns the current processes' pid
|
||||||
|
* `exit/2` takes a pid and a message, and kills the process
|
||||||
|
* `sleep/1` takes a duration in milliseconds and sleeps the process for a time. This isn't strictly speaking necessary, but it is a nice-to-have.
|
||||||
|
|
||||||
|
In Ludus, `receive` will need to be a special form.
|
||||||
|
We could make `self` a reserved word, emitting the instruction to get the current pid.
|
||||||
|
`spawn` I like as a special form: whatever the expression that comes after spawn is just deferred into the new process.
|
||||||
|
So in place of Elixir's `spawn(fn -> :something end)` in Ludus, we'd have `spawn :something`.
|
||||||
|
`send`, `exit`, and `sleep` are a little more interesting (difficult).
|
||||||
|
|
||||||
|
Each could be like `and` and `or`: special forms that _look_ like functions, but really aren't--implemented in the compiler.
|
||||||
|
|
||||||
|
Alternately, I could attempt to find a way to expose the vm to base.
|
||||||
|
That seems, frankly, rather more daunting in Rust.
|
||||||
|
|
||||||
|
But we could also just build out syntax?
|
||||||
|
For example, I had proposed the following desugaring:
|
||||||
|
`foo ::bar (baz)` -> `send (foo, (:bar, baz))`
|
||||||
|
I don't mind the sugar, but it feels like it's actually really important conceptually for Ludus that everything really is functions.
|
||||||
|
(Then again, the sugar feels weird, because `foo` is just a name bound to a keyword, so it's possible to do: `:foo ::bar (baz)`, which makes sense and is weird.).
|
||||||
|
|
||||||
|
The only disadvantage with making `send` a special form is that you can't pass it as a higher-order function.
|
||||||
|
`and` and `or` must be special forms, and don't make sense as higher-order functions because they have a different evaluation order than regular functions.
|
||||||
|
But that's not true of all the things here.
|
||||||
|
|
||||||
|
So how do we connect Ludus function calls to handles in the rust interpreter?
|
||||||
|
In place of base functions, we need something like messages to the process, that aren't mailbox processes but something else.
|
||||||
|
So, like, a special sort of Ludus value, only available in base, that represents the VM.
|
||||||
|
Call it `base :process`.
|
||||||
|
We call it like a function, but the VM instead responds to the various prompts.
|
||||||
|
Thus we can use it to communicate with the process.
|
||||||
|
`receive` will be tricky.
|
||||||
|
But everything else? Seems pretty straightforward.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
:foobar
|
let true = false
|
||||||
|
|
|
@ -235,11 +235,9 @@ impl World {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn run(&mut self) {
|
pub fn run(&mut self) {
|
||||||
loop {
|
|
||||||
if self.active.is_none() {
|
|
||||||
let main = self.zoo.catch(self.main);
|
let main = self.zoo.catch(self.main);
|
||||||
self.active = Some(main);
|
self.active = Some(main);
|
||||||
}
|
loop {
|
||||||
self.active.as_mut().unwrap().interpret();
|
self.active.as_mut().unwrap().interpret();
|
||||||
match self.active.as_ref().unwrap().result {
|
match self.active.as_ref().unwrap().result {
|
||||||
None => (),
|
None => (),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user