Dart Result Monad Library Update 2.1.0: The withResult Method

I just recently pushed an update to my Dart Result Monad Library bumping the version from 2.0.2 to 2.1.0. For this version I added a feature that I’ve kept running into with my development of Relatica: wanting to do something non-transformative with a result and then just pass it on to the next stage. For full details on Result Monads and my initial implementation check out my original blog post and/or this post on the Dart Result Monad 2.0 release updates .

In a lot of cases there is statusing, logging, caching, or something along those lines that I want to see for an intermediate value not just for the end result value. A way around that was to just make a multi-line andThen or andThenSuccess clause and return the passed in object:

final result = doSomething()
    .andThen((r1) => r1.doSomething1())
    .andThen((r2) {
        print(r2);
        return r2;
    })
    .andThen((r2) => r2.doSomething2())

It’s not the prettiest thing in the world but it’s not exactly sexy. It could also be argued that something like that could be done in the preceding step. However I like the fall-through in the chaining of operations that Result Monads naturally get me. I therefore prefer not to have code that reads like:

final result = doSomething()
    .andThen((r1) {
    final newResult = r1.doSomething1();
        if (newResult.isSuccess) {
            print(newResult);
        }
        return newResult;
    })
    .andThen((r2) => r2.doSomething2())

It also becomes very repetitive at best and hard to read intention at worst. What we really mean to say is that we want to do something with the result and then, in terms of the result chain, continue on as if nothing happened. That’s where the withResult and withResultAsync methods come in with this recent release. Now that code is represented as:

final result = doSomething()
    .andThen((r1) => r1.doSomething1())
    .withResult(r2) => print(r2))
    .andThen((r2) => r2.doSomething2())

Any transformations of the passed in value do not propagate to the next stage. The result is only called if it has a success result at that point in the process. If your code generates an uncaught exception however it will short circuit the chain as an error result moving forward. So:

final result = Result.ok('Hello')
    .withResult((s) => print(s[s.length + 1]))
    .andThenSuccess((s) => '$s World');

Will not give you a success result monad with the value “Hello World” inside but instead an error result monad with a RangeError exception inside. The above examples show it being used without the asynchronous operations but it exists for the asynchronous operations as well.