Blocking Procedures

Whereas other languages with some form of builtin concurrency mechanism may tend to make it harder to write async code than blocking code, Claro is very intentional about inverting that balance. Make the good things easy and the bad things hard. So, you may write blocking code in Claro, but as it's really only intended to be used in limited contexts, Claro forces your hand. Any procedure that makes use of the <-| operator either directly or indirectly, must be explicitly annotated to be blocking:

Fig 1:


blocking function doBlocking(x: int) -> Bar<Foo> {
  # ...do stuff...
  var unwrappedGraphRes: Foo <-| fooGraph(x);  # <-- Blocking unwrap.
  # ...do stuff using `unwrappedGraphRes`...
  return # ...
    Bar(unwrappedGraphRes);
}

Graph Procedures May not Call any Blocking Procedures (Directly or Indirectly)

To prevent deadlocking, procedures annotated blocking may not be called from a Graph:

Fig 2:


graph function attemptToDeferToBlockingFn(x: int) -> future<Bar<Foo>> {
  root noopRes <- @defer;
  node defer   <- doBlocking(x);  # <-- Illegal call to blocking procedure in Graph.
}

Compilation Errors:

Graph Function attemptToDeferToBlockingFn function<int -> future<Bar<Foo>>> has illegal transitive dep on the following blocking procedures [doBlocking blocking function<int -> Bar<Foo>>]. Blocking is forbidden within a Graph Function in order to avoid deadlocking.
1 Error

Therefore, you can be confident that the threading implementation of any logic defined within a Graph Procedure will certainly not suffer from liveliness issues in the form of deadlocks (of course, you may still write code with bugs such as infinite loops that may lead to a "livelock").