-
- Notifications
You must be signed in to change notification settings - Fork 653
Description
Expected behavior: Option<>.collect() calls PartialFunction passed as argument only for the value on which this PartialFunction is defined.
Observed behavior: PartialFunction called on values for which it is not defined, resulting in undesired behavior (i.e. unchecked exception thrown).
Example code which fails with arithmetic exception (division by zero):
@Test void collect() { PartialFunction<Integer, Integer> undefinedFor0 = new PartialFunction<>() { private static final long serialVersionUID = 1L; @Override public Integer apply(Integer t) {return 1/t;} @Override public boolean isDefinedAt(Integer value) {return value != 0;} }; Option<Integer> zero = Option.some(0); assertTrue(zero.collect(undefinedFor0).isEmpty()); } Using io.vavr:vavr:0.10.2
I've checked the code and this is what I've found:
-
Optiona<>.collect() eventually calls PartialFunction.lift(), here
return flatMap(partialFunction.lift()); -
in turn, PartialFunction.lift() looks like this:
default Function1<T, Option> lift() {
return t -> Option.when(isDefinedAt(t), apply(t));
}
— meaning apply(t) will be always called, no matter if isDefinedAt(t) returns true or false.
| return t -> Option.when(isDefinedAt(t), apply(t)); |
The proposed fix: changing the code to
default Function1<T, Option<R>> lift() { return t -> Option.when(isDefinedAt(t), () -> apply(t)); }