Considerations
Some thoughts and considerations that led to design decisions.
Promise specs
There are quite a few implementations out there. All with their strength and weaknesses and adapted to the language their written in. It's tempting to follow an existing spec like Promises/A+. I have chosen not to follow this because I think it doesn't play well with Kotlin.
For instance, the Promises/A+ spec defines then as
promise.then(onFulfilled, onRejected)
where onFulfilled and onRejected are two function arguments. Both are basically bind methods on either the
left or right side of a promise. This introduces some challenges that are hard to overcome. The biggest one is how
to handle errors thrown from the onFulfilled or onRejected method. The Promises/A+
states the following:
If either
onFulfilledoronRejectedthrows an exceptione,promise2must be rejected witheas the reason.
This means 1 promise can actually resolve in 3 states instead of the probably expected 2, which are:
- fulfilled with value
- rejected with reason
- rejected by error from either
onFulfilledoronRejected
I don't think this makes much sense and is not what expected when using the API. Therefor the signature for Kovenant's
then function is:
fun <V, R> Promise<V, Exception>.then(bind: (V) -> R): Promise<R, Exception>
Any Exception from the bind method is considered a rejection and a successful computation a fulfillment. That way
the Promise resolves in just 2 states. Kovenant's then behaves quite similar to Option/Maybe/Try types for that matter.
So reason enough to not follow the Promises/A+ spec.
Top level functions
One important consideration is the use of top level functions versus class or
object bound functions. To
illustrate:
task {
foo()
}
//versus
Kovenant.task {
foo()
}
the latter has the advantage that it doesn't interfere with other frameworks that introduces such methods. But on the other hand how many frameworks bringing async functionality does a project really need? So I decided to stick with top level functions, which need to be imported explicitly anyway, to keep things simple.
sun.misc.Unsafe
I started off with an implementation that was partly written in Java and used the sun.misc.Unsafe class. The upside
was that it was quick and very memory efficient. The downside that it requires proprietary API. So I decided that
using a generic code base outweighs the performance and memory efficiency gains. As a bonus everything is written in
pure Kotlin now.