Z: Concurrency
Some rough ideas for a tiered system of concurrency in Z. No syntax, definite semantics or pretty much anything here represents a genuine design proposal. Instead, this sketches out an idea motivated by the observation that concurrency typically comes in a few, distinct fashions, and that using a single primitive (like fire-and-forget threading) is less than ideal.
There are three “tiers” of concurrency – actors, tasks and local parallelism. Local parallelism consists of iterators that have been parallelised and parallel blocks.
fun main()let cubes =parallel for iin 0 upto 5 => i **3 println(cubes)end fun traverse (self :Tree )case selfis binary(let left,let right)parallel do left.traverse()and do right.traverse()and do println(self.data)end else null end end case end traverse
The parallel
keyword instructs the language to optionally
perform whatever code it encompasses in parallel if possible. This is
meant to be a lightweight operation, and the compiler may choose to
emit sequential code (though with no restrictions on ordering).
Tasks are more slightly more heavyweight, akin to either runtime-scheduled, OS-scheduled or M:N scheduled threads. Tasks may be declared within a function-like scope, and such a scope will only return once all its contained tasks have terminated.
fun main storage.store(5) println(storage.fetch())where type Storage =task fun store(x:Int )fun fetch:Int end let storage: Storage =task let valueaccept store(x) value = xend storeaccept fetchreturn valueend fetchend task end main
Tasks are ran concurrently, potentially with a mechanism for data-sharing/synchronization similar to Ada's entries.
Finally, actors are isolated processes/system threads to which messages can be sent in an asynchronous, non-blocking manner and which handle those messages in a single-threaded fashion (unless some other concurrency primitive is used).
actor Counterfun inc(by:Int )set value += byend fun displaydo println(value)end private var value:Int =0 end fun maindo Counter.inc(5 )do Counter.display()end
Local parallelism is useful for spawning array computations or for easily doing calculations in parallel. Tasks are more suitable for situations where a semi-cooperative mode of concurrency is appropriate, and where a procedure needs to do several things in some unspecified order. Finally, actors are suitable for long-running background tasks which don't need to be updated synchronously with the rest of the program, like databases, UI threads or language servers.