Vavr: Consider adding .filterMap() or alternative .collect()

Created on 24 Jul 2019  路  6Comments  路  Source: vavr-io/vavr

Would be nice with either a .filterMap() method or an alternative .collect()!

Examples:

Option.of(1)
  .map(i -> (String) null)
  .filterMap(Objects::nonNull, String::intern);

or alternatively:

Option.of(1)
  .map(i -> (String) null)
  .collect(Objects::nonNull, String::intern);

(Yes, I've read http://blog.vavr.io/the-agonizing-death-of-an-astronaut/ but I believe this is a slightly different topic as one could use any predicate, not just Objects::nonNull.)

I realize there is also:

Option.of(1)
  .map(i -> (String) null)
  .collect(Function1.of(String::intern).partial(Objects::nonNull));

but it's not that easy to read and has a lot of boilerplate.

For Vavr's Option, this could be implemented as:

default <U> Option<U> filterMap(Predicate<? super T> predicate, Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(predicate, "predicate is null");
    Objects.requireNonNull(mapper, "mapper is null");

    return isEmpty() || !predicate.test(get()) ? none() : some(mapper.apply(get()));
}

or alternatively for.collect(), something like:

default <U> Option<U> collect(Predicate<? super T> isDefinedAt, Function<? super T, ? extends U> function) {
    ...create PartialFunction from Function + Predicate, then call the other .collect()?
}
question

Most helpful comment

Thank you for the suggestion, I also think, currently it is hard to create partial functions. Your suggestion is a practical alternative. I will think about it!

All 6 comments

Thank you for the suggestion, I also think, currently it is hard to create partial functions. Your suggestion is a practical alternative. I will think about it!

There's already filter + map which is pretty nice because it uses the basic building blocks.

Option.of(1)
    .map(i -> (String) null)
    .filter(Objects::nonNull)
    .map(String::intern);

Well yes, obviously. But one can say that about so many things. Factory methods, convenience-shortcuts/overloading etc. Why not use just the most basic blocks everywhere?

image

Because you want less boilerplate in your business code!

A factory method for PartialFunction could be something as well.

Option.of(1)
  .map(i -> (String) null)
  .collect(PartialFunction.of(String::intern, Objects::nonNull));

I don't see

.filterMap(Objects::nonNull, String::intern)

as less boilerplate than

.filter(Objects::nonNull).map(String::intern)

@nfekete is absolutely right. Martin Odersky, the creator of Scala, once said: "(...) functions that take two or more function arguments are considered to be 'bad practice'". We stated this in the past over and over again (for example here).

Instead of working around PartialFunction's factory methods, we might deprecate it and the API associated with it, because it isn't practicable. In this example, collect(). PartialFunction was mainly introduced because of Vavr's pattern matching, which will be deprecated in the upcoming major release. This is our main strategy:

Screenshot 2019-07-26 at 01 15 41

(Note: "slang" refers to "Javaslang", the former name of Vavr)

What N谩ndor correctly stated is part of our _purification_ effort:

A healthy, approachable and user-friendly API has a small surface area. Less methods and types are easier to remember and better to maintain.

You can read more here.

Therefore I will close this issue. I also see no benefit of adding aliases to existing methods and blowing up our API surface area.

/thx N谩ndor for pushing me in the right direction, again!

@mandrean just for the sake of completeness - above, you gave a great example of a way too complicated API in our current version 0.10.1.

The future looks more like this (for example Try):

Screenshot 2019-07-26 at 02 59 09

Was this page helpful?
0 / 5 - 0 ratings