Programming languages have long-since learned to short-circuit sequential operations. In damn near any language, if I do the following:
thing one && thing two
I only get to thing two if thing one evaluates true. Even if you’re not a programmer, you probably do this in practice. Consider:
If it’s dry and warm, we’ll play outside.
Which means, if I get to the first part of the sentence, and see that it’s raining, I can just kind of disregard the rest. I don’t have to even read to see what we were going to do if it were dry and warm, because it’s not dry. We do the same sort of thing with “or.” Consider:
We’ll play outside if it’s wet or if it’s dry.
If it’s wet outside, we don’t even need to read the last part of that sentence. We short-circuit the final operation, because it’s not necessary for evaluating the truth of the whole construct. The short-circuiting pattern is important for computers, because in the best-case scenario, it can save a some of computation.
In fact, if you imagine a sentence that includes a list to be evaluated in short-circuit mode, it can save a whole lot of computation:
We’ll play outside if it’s wet or if it’s dry or if it’s cold or if it’s hot or if there’s a hurricane.
Chances are, we’re going to play outside, and if it’s wet, we save the trouble of evaluating all the subsequent possibilities.
Now, for computers, there are some downsides to doing this. For instance, it’s expected that in order to save time and in order to keep everything consistent, these operations need to happen sequentially. A && B && C is different than B && C && A, even if the operations don’t have any side effects. If there are side effects of the computations, the situation is even worse, since in one case maybe you get the side effects of A and B, and in the other you get the side effects of B and C.
If you’re not a programmer, you can skip the part about side effects without any side effects, I think.
What we’re missing is a systematic way of thinking about short-circuit operations that can happen in any order, and can happen either sequentially or out of sequence or concurrently. It’s not that there isn’t special built-in machinery to do this in particular languages, but for a polyglot like me, there’s very little overlap between one way of thinking about it and another.
The funny thing is, English has a great many ways of designating a list as a short-circuit concurrency operation:
- any
- all
- none
- one
- some
- more
- fewer
If you’re going to the store and I give you a list, I can gesture at the list with any of these words to let you know when you can quit. ”Any” of the items means you can stop when you find the first one. ”All” means you can get them in any order, but you need to get all of them.
We should be able to do the same thing, specifying a kind of concurrency and a kind of short-circuiting in programs with the same kind of shorthand. Imagine I have a list of operations to perform, but I only care that at least six of them happen. Or I know that if any of them fail, I can stop all of them. Or if any of them succeed I can stop all of them.
I think that’s a level at which a programmer (at least the kind of programmer I am, which is to say one with more hubris and laziness than usual) should interact with a language’s underlying concurrency model.
Larry Wall put this idea into Perl 6, though I’m not convinced the semantics are exactly right. For instance, I’m not sure if I should generally be saying:
someMethod(any(1,2,3))
or
any(someMethod(1), someMethod(2), someMethod(3))
The former looks terse at first glance, but requires a lot of bookkeeping inside of someMethod that might be avoided, particularly in languages that don’t have the notion of a Junction (that’s what he calls this kind of construct) baked in. The latter looks verbose at first glance, but one can imagine cases where it would be useful to evaluate several different methods and/or put some syntactic sugar around the actual call.
I can also imagine a kind of chaining of these terms together in a way that creates a massively concurrent operation that can return quite quickly in the best-case scenario:
if any(all(none(1,0,0), all(1,1,1), one(1,1,1)), any(1,0,1))
then someMethod()
This could use the same truth-logic as usual boolean algebra, but could also short-circuit efficiently in a way that would be more difficult (or require more sophisticated machinery) if those same calls were the usual ands and ors that we would use in such code.
I don’t think the machinery for this is too complicated. It would, of course, matter how the underlying concurrency was managed. In something like Go or Erlang, it would probably be quite fast, and in something that required a heavier process for each unit of concurrency, it would probably only be worthwhile in the case of heavy computations that could benefit from the short-circuiting.
Last, but not least, I can imagine mixing this together with a bit of map-reduce. So, if I do something like:
all(someMethod(“foo”), someMethod(“bar”))
I can write these methods to also stash a result somewhere (nasty side-effect, that) but only use it if the computations complete successfully according to the all operator (e.g. none of them return false and all of them return).