Stream.flatMap reference ambiguity
david at hartveld.net
Wed Feb 27 06:43:40 PST 2013
As an API user, I think that it is important to limit the situations in
which ambiguity can occur. Better to change the API design, versus having
to disambiguate 'by hand' as API user. If this has to happen at the cost of
renaming some methods to a longer form (e.g. flatMap -> flatMapToInts, or
apply -> applyAsInt - and these are just examples), I would prefer that.
Why? Because I'd rather type a few extra letters to make my goal explicit
(i.e., specialize the stream to e.g. an IntStream), than to do that
implicitly by specifying types.
BTW, this also makes the code less 'brittle', making it much more forgiving
to refactorings, which is, I think also a big plus.
If the names are changed, it is probably good to document why these differ
in the final documentation (similar to the reasons why there are
specialized streams for primitives in the first place):
- Needed for disambiguation between regular and specialized flatMap
- Makes explicit the specialization to different stream types
- Rationale for this choice: why better than which alternatives?
On Wed, Feb 27, 2013 at 10:45 AM, Paul Sandoz <paul.sandoz at oracle.com>wrote:
> On Feb 27, 2013, at 1:23 AM, Dan Smith <daniel.smith at oracle.com> wrote:
> > On Feb 26, 2013, at 2:57 PM, Zhong Yu <zhong.j.yu at gmail.com> wrote:
> >>> I think the core problem is the API (in general, it's best to avoid
> overloading with functional interfaces that have different parameter types),
> >> I don't quite understand what you mean, can you clarify?
> > The type of the second parameter of the lambda depends on the overload
> resolution choice -- it may be a Consumer<String>, or a IntConsumer, or a
> LongConsumer, or a DoubleConsumer. So overload resolution is given four
> different possible typings of the parameters of the lambda, and is asked to
> choose the "best" one.
> > As I've pointed out, this is a pretty brittle situation, and while the
> compiler is able to do a certain degree of disambiguation, there's a good
> chance that clients of the API will encounter ambiguities.
> > For example:
> > stream.flatMap((x, sink) -> sink.accept(x.length()));
> > 'x.length()' is an int, meaning the intent is probably to represent an
> IntConsumer; but the compiler is not equipped to make that sort of
> judgement, and would be equally happy with a LongConsumer, or a
> DoubleConsumer, or a Consumer<Integer>, ...
> > Hence, API designers should generally avoid overloading that relies on
> disambiguating between functional interfaces of the same arity that have
> different parameter types.
> So could we do either (or both) of the following:
> - change the Int/Long/DoubleConsumer.accept methods to be
> - change the flatMap methods to be flatMapInt etc.
> We don't always disambiguate the method name of the functional interface
> from its primitive specialization counterpart e.g. when the return type is
> void. What you say above suggests that we should since other APIs using
> these interfaces are gonna rub up against similar issues.
More information about the lambda-dev