One of my professors had an idea about distributed synchronous programming, but nothing ever came of it.
One of my professors had an idea about distributed synchronous programming, but nothing ever came of it.
I have had personal experience delivering the technology in the fields. We implemented the entire safety logic of a driverless train control system using SCADE (hence esterel). Airbus uses it for their onboard computers. So as you can see, this has had massive application around the world.
We worked on a hackathon together where we built a physical chessboard interface (https://github.com/chesseye/chesseye). He built the controller, which handles among other things the output from the video recognizer and messages the chess engine, in ReactiveML. I didn't know much about the language beyond first principles then, but was impressed by how easy it made it to compose parallel processes.
We tried to convey some of those conclusions in a later presentation, even though looking at it 2 years later I realize it's probably hard to get the insights without the verbal delivery: https://github.com/chesseye/chesseye/blob/master/presentatio...
(I understand there was a ReactiveML tutorial at ICFP last Saturday: https://icfp18.sigplan.org/program/program-icfp-2018, not sure if this post is related.)
It shouldn't be too difficult to distribute synchronous languages, and it is a quite natural idea. Most have a semantics based on Kahn networks [1], which is already a distributed model of computation :).
I don't think this is correct. Kahn networks are asynchronous (and therefore well-suited for distributed systems), but synchronous languages are – well, synchronous.
For every pair of nodes that exchange information, there has to be a rendezvous between them after every non-instantaneous step [1], which is terrible for performance in a distributed system (but virtually free in typical single-clocked, synchronous digital circuits).
[1] You can devise heuristics which reduce the frequency of those rendezvous in some cases, but conceptually, they're still there.
However, there is a notion of synchronous Kahn networks, but maybe it is only used by functional synchronous and dataflow languages (e.g., Lucid Synchrone, ReactiveML). Compilation of such languages involve clock calculus (i.e. typing for scheduling) which I think makes it possible to compute the maximum size of a queue in the network (or maybe at least guarantee that the size is bounded?). This would mean that what you call rendezvous points would be statically computable (in the case of pure synchronous languages it should be easy as the maximum queue size is zero, or one depending on how we count).
Anyway, you're right that I was mistaken to think that it was an easy problem.
EDIT: Heh: https://www.lri.fr/~mandel/papiers/MandelPlateauPouzet-MPC-1... :).
#include "arduino/arduino.ceu"
input int PIN_02; // button input
output int PWM_05; // LED outputs, remember that pins 5 and 6
output int PWM_06; // have a higher PWM frequency on the UNO
// a code block that concurrently fades an LED in and out
// `pin` the output pin of the LED
// `min` min value of the fade
// `max` max value of the fade
// `delay` ms to wait between `analogWrite` updates
code/await Fade_forever(var u8 pin, var u8 min,
var u8 max, var uint delay) -> void do
loop do
var int i;
loop i in [min->max[ do // fade in loop
if pin == 5 then
emit PWM_05(i);
else/if pin == 6 then
emit PWM_06(i);
end
await delay ms;
end
loop i in [min<-max[ do // fade out loop
if pin == 5 then
emit PWM_05(i);
else/if pin == 6 then
emit PWM_06(i);
end
await delay ms;
end
end
end
loop do //endless loop
// if *any* of these three code blocks (trails) end,
// all of the remaining trails in a `par/or` are
// aborted and code resumes (in this case, the outer
// loop restarts). By comparison, a `par/and`
// would require *all* of the trails to terminate.
par/or do
// if a button is pressed, reset the loop
await PIN_02;
with
// fade the LED at pin 5 quickly between 64 and 192
await Fade_forever(5, 64, 192, 5)
with
// fade the LED at pin 6 slowly between 0 and 256
await Fade_forever(5, 0, 256, 20)
end
Pretty simple and readable code, don't you think? (Note the use of Bourbaki interval notation for [min -> max[ - how often have you seen that in programming languages?) Now imagine what the plain C version of this would look like.What I also find fascinating about it is that it has almost no memory overhead per trail - a handful of bytes IIRC. Compare that to the kilobytes required for green thread solutions elsewhere. OTOH, computationally the concurrency doesn't really scale with large numbers of trails - I think you could compare it to insertion sort: unbeatable for small arrays due to low overhead, but then that O(n^2) takes over.
One might envision having many small Céu programs with only a handful of trails each, asynchronously running in their own threads and interacting with each other. From what I gather, programming in this model is called using the GALS principle: Globally Asynchronous, Locally Synchronous. For the intended environment for Céu (embedded programming), you kind of "naturally" get this: asynchronicity just kind of "happens" due to separate pieces of hardware interacting.
Anyway, it kind of feels like the "missing element" in reactive and concurrent programming paradigms to me. I hope more languages will start picking it up eventually.
[1] https://www.reddit.com/r/arduino/comments/5qzv12/c%C3%A9uard...