Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature request] If Either<Failure, Success>, how can I know what kind of Failure happened. #119

Open
JCKodel opened this issue Feb 14, 2023 · 1 comment

Comments

@JCKodel
Copy link

JCKodel commented Feb 14, 2023

Scenario:

  • I have a method that returns Either<Failure, Success>.
  • Failure is a base class for "I don't have a clue what happened"
  • I can extend Failure to be more precise, for instance:
class NumberOutOfRangeFailure extends Failure {
  const NumberOutOfRangeFailure({required this.minValue, required this.maxValue});

  final int minValue;
  final int maxValue;
}
  • So, when needed, I can return const Left(NumberOutOfRangeFailure(minValue: 1, maxValue: 99)), meaning: you can only chose between numbers 1 and 99
  • Now, how can I display an error message to the user, since my method returns the base Failure class, without using if or switch?

What about:

static void _dispatchCommand({required BuildContext context, required Command command}) {
  final result = command.dispatch(context);

  result.leftMap(
    (failure) {
      final errorMessage = match<Failure, String>(failure)
          .when<GetNumberedQuoteCommandEmptyNumberFailure>((f) => "Number should not be empty")
          .when<GetNumberedQuoteCommandInvalidNumberFormatFailure>((f) => "Number is not correctly formatted")
          .when<NumberShouldBePositiveFailure>((f) => "Number should be positive")
          .when<NumberShouldBeLessThan100Failure>((f) => "Number should be < 100")
          .when<NumberOutOfRangeFailure>((f) => "Number should be between ${f.minValue} and ${f.maxValue}")
          .otherwise((f) => "Unknown ${f} error");

      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(errorMessage),
        ),
      );
    },
  );
}

In the above code, I just ignore the Success (right), because, well, I don't need to inform the user about it, but for Failure (left), I can test it using generics and have typed failures to create my error message string (notice how I interpolate the minValue and maxValue in the error message).

How?

Using this code (based on #48):

extension Matching<L, R> on Either<L, R> {
  Either<L, R> on(bool Function(R r) predicate, L Function(R r) value) => flatMap((r) => predicate(r) ? left<L, R>(value(r)) : right<L, R>(r));
  Either<L, R> when<T extends R>(L Function(T r) value) => flatMap((r) => r is T ? left<L, R>(value(r)) : right<L, R>(r));
  L otherwise(L Function(R r) transformation) => fold(id, transformation);
}

Either<Result, A> match<A, Result>(A a) => right(a);

So now I can filter my result (by bool predicate), or I can match types using my when extension.

Anyway, my feature request is to incorporate that when, on and otherwise into Either<L, R>.

@jacksonb-cs
Copy link

It sounds like your problem could easily be solved by making Failure a sealed class and using a switch statement. But, if you don't want to do that, you could add an abstract method to Failure, which would require all implementing classes to override said method.

abstract class Failure {
  String getFailureMessage();

  ...
}

class NumberOutOfRangeFailure extends Failure {
  final int minValue = ...;  // Get these values however you want
  final int maxValue = ...;

  @override
  String getFailureMessage() {
    return "Number should be between ${minValue} and ${maxValue}";
  }
}

Then you can just use any instance of a subclass of Failure to call getFailureMessage().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants