spebbe / dartz

Functional programming in Dart
MIT License
756 stars 59 forks source link

Function generator callback with Option.fold #59

Closed tdnghia98 closed 4 years ago

tdnghia98 commented 4 years ago

Hi all,

I love using the dartz package but today in my use case, I discovered that the callback is not executed when using Option.fold

currentUserOption.fold(
            () => throw NotAuthenticatedError(),
            (user) async* {
              // Method never got executed
              print('User is some');
              print('Submitting');
              final updatedUser =
                  user.copyWith(enterpriseFormAnswer: state.answer);
              failureOrSuccess = state.isEditing
                  ? await _userRepository.update(updatedUser)
                  : await _userRepository.create(updatedUser);

              yield state.copyWith(
                  isSaving: false,
                  saveFailureOrSuccessOption: optionOf(failureOrSuccess));
              print('done');
            },

Is there any support on adding this to the next release? This is one of the very useful feature when dealing with options in the bloc

spebbe commented 4 years ago

Hi @tdnghia98 and thanks for the support!

Are you consuming any elements from the Stream returned by fold? Depending on how you construct your Stream, you might have to consume at least one element from the returned Stream before the body of the function even starts getting evaluated.

Maybe this snippet will shed some light on things?:

Stream<String> characters(Option<String> maybeS) async* {
  yield* maybeS.fold(() async* {}, (s) async* {
    print("starting...");
    for(int ix = 0;ix < s.length;ix++) {
      yield s.substring(ix, ix+1);
    }
  });
}

main() async {
  print("Consume nothing:");
  await for(final c in characters(some("hello")).take(0)) {
    print(c);
  }

  print("Fully consume:");
  await for(final c in characters(some("hello"))) {
    print(c);
  }
}
apiep commented 4 years ago

Or a specific code for bloc, you can always yield* fold like this

abstract class Event {}

class OptionProducingEvent extends Event {
  final Option<String> data;
  OptionProducingEvent(this.data);
}

class CounterBloc extends Bloc<Event, int> {
  CounterBloc() : super(0);

  @override
  Stream<int> mapEventToState(Event event) async* {
    if (event is OptionProducingEvent) {
      // Notice here we `yield*` data returned from fold
      yield* event.data.fold(() async* {
        // Not sure if it safer to use empty stream function `() async* {}`
        // or Stream.empty()
        yield* Stream.empty();
      }, (realData) async* {
        yield realData.length;
      });
    }
  }
}
spebbe commented 4 years ago

Hope that helped – closing for now.

tdnghia98 commented 4 years ago

Sorry for the late reply. Actually the problem is the callback function signature does not allow a Future return, I am executing async code in the body of the callback.

tdnghia98 commented 4 years ago

OK I got why. I made the function async so I need to await for it. Here is the code in case someone has the same issue.

Thanks for your help!

yield await signedUpUser.fold(
            (f) {
              return state.copyWith(
                isSubmitting: false,
                authFailureOrSuccessOption: some(
                  left(
                    const AuthFailure.serverError(),
                  ),
                ),
              );
            },
            (user) async {
              await _userRepository.create(
                User.signedUp(
                  id: user.id.getOrCrash(),
                ),
              );
              return state.copyWith(
                isSubmitting: false,
                authFailureOrSuccessOption: some(failureOrSuccess),
              );
            },
          );
        }