Type safe actor API
The API exposed by std::process
relies on dynamic typing when sending and receiving messages, preventing the compiler from ensuring type safety. For example, to send and receive a process you would write:
import std::process
let pid = process.spawn {
let message = process.receive
...
}
process.send(pid, 'hello')
If you don't want to have a dynamic type upon receiving you will need to perform some kind of type check, then cast the data:
import std::process
import std::vm
let pid = process.spawn {
let message = process.receive
message.kind_of?(String).if true: {
let string = message as String
...
}, false: {
vm.panic('oops!')
}
}
process.send(pid, 'hello')
This gets tedious very quickly. As such Inko should provide a slightly more high-level and type safe API on top of processes. A simple example would be the following:
import std::process
object Sender!(T) {
def init(pid: Integer) {
let @pid = pid
}
def send(value: T) -> T {
process.send(pid: @pid, message: value)
}
}
object Receiver!(T) {
def receive -> T {
process.receive as T
}
}
def spawn!(T)(block: lambda (Receiver!(T))) -> Sender!(T) {
let pid = process.spawn {
process.receive.call(Receiver!(T).new)
}
process.send(pid: pid, message: block)
Sender.new(pid)
}
let sender = spawn!(Integer) lambda (receiver) {
receiver.receive + 5 # => OK
receiver.receive.foo # => error since "foo" is not defined for Integer
}
sender.send(10) # => OK
sender.send('foo') # => error since our Sender operates on an Integer
With the upcoming changes to the type system (!1 (merged)) this should work. It's a bit more verbose due to requiring explicit type signatures when calling spawn
, but apart from that it's a very simple solution.
Of course technically one could still send any message to the process spawned using this API. However, by passing around the Sender instead of a raw PID this should only happen when somebody deliberately bypasses these APIs.