0

I have a method deleteUser() which returns a custom object Response

In this method I am calling method tryDelete() which returns a Mono<Boolean>. In order to act on the boolean in a non-blocking way I'm using .flatMap() with an if / else statement.

@Override
public Response deleteUser(String accountId) {
    return tryDelete(accountId).flatMap(isSuccessful -> {
        if (isSuccessful) {
            return buildResponse("happy happy");
        } else {
            return buildResponse("not good");
        }
    });
}

tryDelete()

public Mono<Boolean> tryDelete(String accountId) {
    return Mono.just(true);
}

However, it doesn't compile and I get

no instance(s) of type variable(s) R exist so that Response conforms to Mono<? extends R>

Kaigo
  • 1,267
  • 2
  • 14
  • 33

3 Answers3

2

flatMap argument is a function I -> Mono<O>, not a function I -> O where I and O are respectively the input and the output types.

If you don't want to extract the content from the Mono, you should wrap the Response in a new Mono.

public Mono<CustomResponse> deleteUser(String accountId) {
    return tryDelete(accountId).flatMap(isSuccessful -> {
        if (isSuccessful) {
            return Mono.just(buildResponse("happy happy"));
        } else {
            return Mono.just(buildResponse("not good"));
        }
    });
}

public CustomResponse deleteUserBlocking(String accountId) {
    Boolean isSuccessful = tryDelete(accountId).block();
    if (Boolean.TRUE.equals(isSuccessful)) {
        return buildResponse("happy happy");
    } else {
        return buildResponse("not good");
    }
}

Are you sure you don't just want to use map (which actually has a I -> O as argument)?

public Mono<CustomResponse> deleteUser(String accountId) {
    return tryDelete(accountId).map(isSuccessful -> {
        if (isSuccessful) {
            return Mono.just(buildResponse("happy happy"));
        } else {
            return Mono.just(buildResponse("not good"));
        }
    });
}
dcolazin
  • 831
  • 1
  • 10
  • 25
  • If I use map I need to block() which is not ideal. – Kaigo Sep 01 '23 at 09:21
  • I also forgot to mention the deleteUser() method is overriden so I can't change the signature. – Kaigo Sep 01 '23 at 09:22
  • If you have to return a Response and the method is not somehow async, then you have to block it somewhere... – dcolazin Sep 01 '23 at 10:13
  • I think `.doOnSuccess()` with `.subscribe()` would be non-blocking – Kaigo Sep 01 '23 at 12:03
  • Look at the arguments and return types of doOnSuccess and subscribe: they are not your desired output. deleteUser returns an actual Response object, not a CompletableFuture or a Mono; so, when deleteUser must be blocking. – dcolazin Sep 01 '23 at 12:21
  • Ah yes I see what you mean now. It has to block eventually to return (as it's not returning Mono). I still think using subscribe() would result in less waiting.. – Kaigo Sep 01 '23 at 17:21
0

I think it would be best not to return Mono on tryDelete method and do something like that:

public Optional<Account> tryDelete(String accountId) {
    if (repo.existsById(accountId))
        return Optional.of(repo.deleteById(accountId));
    else
        return Optional.empty();
}

public Mono<Response> deleteUser(String accountId) {
    Account deletedAcc = tryDelete(accountId)
                          .orElse(null);

    boolean deleted = deletedAcc == null ? false : true;

    if (deleted)
        return Mono.just(buildResponse("good"));
    else 
        return Mono.just(buildResponse("not happy"));

}

0

Because I am learning haskell, and I know that there is monads, and I know that Optional in Java is monad, and maybe Mono in your code is a monad too, I really want to answer this question.

I learned from a video that monad is approximately equal to

  1. functor with flatten, or
  2. flatMap

In my opinion, monad is some kind of container/box that hold object with extra state of that object.

The example code in the video, is about parsing a string and return a parsed, divided number. Return of the function will be Optional.empty if any of the step is not finished(like parse non digit string to number, in another words, value in Optional is not presented):

public Optional<Double> codeWithoutFlatMap(String s){
    final Optional<String> split = split(s);
    if (split.isPresent()) {
        final Optional<Double> parse = parse(split.get());
        if (parse.isPresent()) {
            final Optional<Double> result = divide(parse.get());
            if (result.isPresent()) {
                return result;
            }
        }
    }
    return Optional.empty();
}

With flatMap, the code is much more precise

// this code is exactly equal to code before
public Optional<Double> codeWithFlatMap(String s){
    return split(s)
            .flatMap(split -> parse(split))
            .flatMap(parse -> divide(parse));
}

Back to your case, I notice some code can be improved, it is the cause for not compiling:

  1. flatMap returns boxed type, for example Optional<Response> or Mono<Response>, you expect method would return a Response, but actually it returns a Mono<Response>
  2. the Function inside flatMap, should return a boxed type, like Mono<Response>
Hi computer
  • 946
  • 4
  • 8
  • 19