It indeed is difficult to combine static typing, and actors that can receive messages from any other actor. "Session typing" is a way of handling this, but I've yet to see an implementation that isn't overly complex.
Type-checking messages on the sending side is straightforward: just use a generic PID/actor type of some sort. So for example (using Inko [1] syntax here):
let child: Process!(String) = process.spawn {
...
}
child.send('hello')
Handling this on the receiving end is more tricky, as a receive could happen anywhere at any time. Imagine somewhere deep down you have a method like this: def foo {
process.receive
}
How would the compiler know what type `process.receive` is supposed to return? What if you want not just a string, but a specific list of strings?I'd say that for most languages the best approach is to use some kind of "Any" type for messages, and rely on some form of pattern matching to figure out what you're dealing with. This has the downside of not providing compile-time safety, but it might be good enough. For example, in Inko (this is a new addition not yet released) you would do something like this:
match(process.receive) {
'start' -> { start_the_thing }
'stop' -> { stop_the_thing }
else -> { oops }
}
[1]: https://inko-lang.org/