OP argues that callbacks are less complex than type-checked null because it eliminates checking success at the call site. Branching on success at the call site is similar complexity as implementing onSuccess and onError. I don't think its the check itself that matters, its remembering the check, and both a success/failure interface and type-checked null meet this goal.
As to which is better, type-checked null is referentially transparent, which is a nice objective measurement of complexity, and referentially transparent functions are generally more reusable than those that aren't. It also keeps my mental model of the stack tidy, tail-call optimization or no.
I agree that it doesn't make things easier but makes it easier to remember.
One thing that the callback style enables is that you can have more than just success and failure and/or you can provide more information about why it failed.
I don't particularly like this callback style though. I do like the multi return value style in languages like Go* where, by convention, an error is returned as the last parameter. You still have to check whether the error itself was nil, but at least you get a reason why; It is also part of the method signature, meaning you can ignore it, but you have to explicitly ignore it.
While that doesn't make it referentially transparent, it does force the programmer to acknowledge the possibility of error, even if they don't deal with it. It also keeps the flow more sequential and often times easier to read than the callback style.
*C#, Ruby and others also have some form of multiple-return values, but it's rare to see them used in this way.
the whole point of type-checked null is that the type system (compiler) forces you to acknowledge potential error states. it doesn't care if you deal with it; you can ignore the error states, or not, but its not possible to forget.
As to which is better, type-checked null is referentially transparent, which is a nice objective measurement of complexity, and referentially transparent functions are generally more reusable than those that aren't. It also keeps my mental model of the stack tidy, tail-call optimization or no.
QED, right? have I missed anything?