4 ms·
Type safe process communication is a tough nut to crack. You can sort of handle it by doing away with PIDs or by at least making them generic, but that leaves o
by YorickPeterse 7y ago
Type safe process communication is a tough nut to crack. You can sort of handle
it by doing away with PIDs or by at least making them generic, but that leaves
out corner cases such as sending a message to the current process. Type
inference of the message types is also hard, imagine something like this (using
pseudo code here):
let pid = spawn {
... hundreds of lines of code ...
let message = receive
message + 10
}
... lots of code here ...
send(pid, 20)
A compiler being able to infer the type of the message here will depend a bit on
how it processes code: does it go into the spawn first, or process that later?
If it does go into spawn first, what should it do when it reaches `message +
10`. And what if we were to process `send()` first, but it's actually sending
the wrong type? We'd probably infer the type to be an integer, even if the
receiving end actually wanted something else.
Session types are one approach to dealing with this, but I have yet to see an
implementation of this where it's actually pleasant to describe the session in
the type system; most that I remember turn out horribly complex.
I spent a fair bit of time on this for Inko (https://inko-lang.org/ https://inko-lang.org/) as it has a
similar message sending system. Right now you'd have to use pattern matching to
make things safe (this isn't released yet), but obviously I would like to check
this at compile-time. Sadly I haven't been able to make this work yet, as there
was always some corner case somewhere that meant that whatever I had implemented
at that point wouldn't quite cut it.
For a while Inko had a special API for this that basically worked by sending
types closures to the receiving end, that way type-safety could be enforced by
making sure the right closures were used. Sadly this is not the most efficient,
and in some cases still required type annotations.
One thing I did, but this may not work for Gleam, is to replace PIDs with just
references to processes, and only allow obtaining such references by spawning a
process. By not allowing one to just create a PID out of thin air, you reduce
the likelihood of a process randomly receiving a message of a type it does not
understand. A side benefit of this is not having to allocate PIDs, which I found
out can actually be quite a bottleneck when spawning many processes (though
Gleam being based on BEAM has no real choice in this matter).
- lpil 7y agoThanks for sharing your thoughts! It's quite reassuring to read your post as it sounds like we have had many of the same ideas about types and processes. > One thing I did, but this may not work for Gleam, is to replace PIDs with just references to processes, and only allow obtaining such references by spawning a process. This is my current plan, though it does make interop with Erlang/OTP somewhat more tricky. I'm going to have to redesign some of the behaviours there to make passing around references to processes possible.
- YorickPeterse 7y ago> This is my current plan, though it does make interop with Erlang/OTP somewhat more tricky. I have always wondered just how common it is in Erlang/Elixir to just fabricate a PID out of thin air and start sending messages to it. My gut feeling is that this is quite rare, but then again I'm not an Erlang user. If you want to rubberduck about anything related to this, feel free to send me a message/Email/Tweet/etc :)
- lpil 7y agoCreating the PID data structure is rare however it's common to give names to processes and then throughout the rest of the program make the assumption that there a process behind the given name that will handle the messages you are sending. I think we'll have to avoid this practice altogether.