Elegant error handling in Dart like Scala's `Try`

637

Solution 1

If you are OK with using Future what's wrong with this advice: Using Future.sync() to wrap your code? The code will look like this:

void main() {
  var f = Future.sync(() {AuthUser("email", "token", "username", "bio", "img"); });
  f.then((v) =>  print("Value: " + v.toString())).catchError((e) => print("Failure: " +e.toString()));
}

The main trick is that Future.sync effectively enables lazy evaluation of the parameter but you have to pass your parameter wrapped in a function. This is actually the same trick Scala compiler does for Try (i.e. for call-by-name parameters) but takes adding a few brackets around.

Solution 2

If you only want the basic functionality of returning either type based on whether an exception occurred or not then you can easily create a utility class such as below.

Otherwise I recommend @SergGr's answer about using Future.sync since it gives you more monadic like pipeline.

void main() {
  Try<Error, void> result = Try.it(() => Validate.isEmail("test-example.com"));

  if (result is Success) {
    print("Good");
  } else if (result is Failure) {
    print("Error: " + result.exception().toString());
  }
}

typedef TryExec<V> = V Function();

abstract class Try<E extends Error, V> {

  static Try<E, V> it<E extends Error, V>(TryExec<V> fn) {
    try {
      return Try.success(fn());
    } catch (e) {
      return Try.failure(e);
    }
  }

  static Try<E, V> failure<E extends Error, V>(Error e) {
    return new Failure(e);
  }

  static Try<E, V> success<E extends Error, V>(V v) {
    return new Success(v);
  }
}

class Failure<E extends Error, V> extends Try<E, V> {
  final E _e;
  Failure(this._e);

  E exception() => _e;
}

class Success<E extends Error, V> extends Try<E, V> { 
  final V _v;
  Success(this._v);

  V value() => _v;
}
Share:
637
Ukonn Ra
Author by

Ukonn Ra

Updated on December 07, 2022

Comments

  • Ukonn Ra
    Ukonn Ra over 1 year

    A data class in Dart:

    import 'package:validate/validate.dart';
    class AuthUser {
      final String email, token, username, bio, image;
    
      AuthUser(this.email, this.token, this.username, this.bio, this.image) {
        Validate.isEmail(this.email);
      }
    
      @override
      String toString() {
        return 'AuthUser{email: $email, token: $token, username: $username, bio: $bio, image: $image}';
      }
    }
    

    where Validate.isEmail will throws an Error when failed to match:

    static void matchesPattern(String input, RegExp pattern,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
        if (pattern.hasMatch(input) == false) {
            throw new ArgumentError(message);
        }
    }
    
    static void isEmail(String input,[String message = DEFAULT_MATCHES_PATTERN_EX]) {
        matchesPattern(input,new RegExp(PATTERN_EMAIL),message);
    }
    

    Now I want to use an elegant way to new this class. When using Scala, I can use Try(new AuthUser(...)) and patten-matching it.

    And in Dart, first I tried RxDart,

    void main() {
      testWidgets('Counter increments smoke test', (WidgetTester tester) async {
        Observable.just(AuthUser("email", "token", "username", "bio", "img"))
            .doOnError((e, s) => print("oh no"))
            .listen((e) => print(e.toString()));
      });
    }
    

    Not work, the test failed for the error(which means RxDart doesn't catch errors at all!!!)

    And I want to try Future, failed also.

    And I want to use dartz, but I am worried because there is just one maintainer...

    Any advice?

    • JRomero
      JRomero over 5 years
      RxDart isn't catching the exception because the exception is being thrown before it's part of the Observable. To elaborate, your code is equivalent to final user = AuthUser(); Observable.just(user);. As you can now see the exception is when creating the user.
    • Rémi Rousselet
      Rémi Rousselet over 5 years
      There is no pattern matching in dart
    • Ukonn Ra
      Ukonn Ra over 5 years
      @RémiRousselet I know, I just need a solution to try/catch exception elegantly
    • Ukonn Ra
      Ukonn Ra over 5 years
      @JRomero So any advice?
    • Chris Reynolds
      Chris Reynolds over 5 years
      My immediate reaction is Just Don’t. The language has a specific way of trapping errors and grafting on your preferred method from another language is probably not best practice. However that is a personal opinion.
  • Chris Reynolds
    Chris Reynolds over 5 years
    My immediate reaction is Just Don’t. The language has a specific way of trapping errors and grafting on your preferred method from another language is probably not best practice. However that is a personal opinion.