Comparing Ox & Gears

The gears project is an experimental multi-platform library covering direct style concurrency in Scala. The scope of both Ox and Gears is similar in places, however there are also some notable differences, listed below.

Originally posted on Reddit:

  1. The fundamental difference is in the timeframes and perspectives of the projects. With Ox, we are trying to provide people with the tools necessary to write direct-style Scala now (2024). On the other hand, Gears is more of a research project, and coupled to Project Caprese, which will still run until 2028. That’s not to say that Gears won’t have a stable release before then - we’re not aware of Gears’s release plan - however the development goals of both projects seem different. Gears is more in an exploratory phase, while in Ox we are looking at a shorter time-to-market.

  2. Ox only targets the JVM 21+, while Gears targets JVM 21+ and Native. While we don’t rule out adding Native support, if it will be possible, it’s not our immediate goal, because of (1).

  3. This also influences features such as capture checking. It seems Gears will want to use the capture checker pretty early. We’re hoping to do the same in Ox at some point, but that’s only after the capture checker is relatively complete, and available in a stable (maybe LTS?) Scala release. So this might still take some time.

  4. The scope of Ox is a bit wider than just concurrency: we’re also looking at resiliency and general direct style utilities. One could of course debate, is the specialised-library approach taken by Gears better, or the more broad one taken by Ox. But I don’t think there’s a universal answer to that.

  5. The programming styles that both libraries offer are slightly different as well. In Gears, most of the provided functionalities operate on the level of Gears-Futures. While in Ox, you often operate on thunks => T or () => T. The Gears approach is more general, however the Ox one is more “direct” for the common case.

  6. That might be considered a small detail, but we think naming is important. Gears is centered around a Future abstraction, which is a distinct type from scala.concurrent.Future. This might create unnecessary confusion, plus in our opinion it’s worthy to distinguish between promise-like futures and thread-like futures. That’s why in Ox we’ve got Forks instead (which are a thread-like-future data type). However, you don’t use that type often, because of (5) above.

  7. Another rather fundamental difference is in our approach to error handling. In Ox, when you create a supervised scope with forks inside, if any of the fork fails, the whole scope fails and re-throws this exception (a variant of let-it-crash). In Gears, the default is to have failed futures, and the errors are only discovered when .joining them. Both approaches have their merits, however the Ox one, where you have to explicitly create unsupervised forks (using forkUnsupervised), seems safer: your code might crash, but you won’t miss an error.

  8. Speaking of error handling, Ox provides support for various ErrorModes, that is situations where you want to represent errors-as-values (in addition to exceptions). We propose a specific way to represent such application/logical errors (using Eithers), with the built-in concurrency operators often having Either-variants, and by providing a boundary-break implementation for Eithers. Gears might be getting something similar, so this might stop being an actual difference.

  9. In Gears, there are two capabilities: Async and Async.Spawn. The first one represents a capability to suspend, the second - to fork. In Ox, we only have the Ox capability, which corresponds to Async.Spawn. You don’t need a capability to suspend. This might be seen as a feature, or a bug. On one hand, it might be useful to know that somewhere down the call chain, your code will want to suspend, and more importantly, to be interrupted. That’s the kind of information you get with Async in Gears. On the other hand, our worry is that using Async will be the new implicit ec: ExecutionContext. We’re not ruling out adding an Async-like capability to Ox, it might turn out to be the right thing to do, but we’d still like to explore some other options (as hinted in the Scalar talk, near the end)

  10. Ox’s Kotlin-inspired Channel & select implementation is less flexible than the one in Gears (you can’t nest select’s that easily), however it might be more performant. Initial benchmarks of a WebSocket server using Java 21 & Ox streaming turned out better or matching the asynchronous implementations.