Hi Paul,

The use of BaseStream was just an example, here is another one that works only 
if the function first parameter type is declared as '? super 
Stream<StackWalker.StackFrame>'.

static Function<Stream<?>, Integer> counter() {
  return stream::count;
}

...
StackWalker walker = ...
int count = walker.walk(counter());

regards,
Rémi

----- Mail original -----
> De: "Paul Sandoz" <paul.san...@oracle.com>
> Cc: core-libs-dev@openjdk.java.net
> Envoyé: Lundi 2 Novembre 2015 13:44:24
> Objet: Re: Proposed API for JEP 259: Stack-Walking API
> 
> I agree with Maurizio, the first signature is good enough.
> 
> One could argue that it might be better to apply PECS since it would
> encourage more consistent usage when it is actually required as all too
> often it’s easy to forget, and then too late to change. However, i don’t
> want to encourage the use of BaseStream since it was an unfortunate mistake
> that this made public.
> 
> Paul.
> 
> > On 2 Nov 2015, at 13:26, Maurizio Cimadamore
> > <maurizio.cimadam...@oracle.com> wrote:
> > 
> > So, we have three potential signatures here:
> > 
> > <T> T walk(Function<Stream<StackWalker.StackFrame>, T> function) //1
> > 
> > <T> T walk(Function<Stream<StackWalker.StackFrame>, ? extends T> function)
> > //2
> > 
> > <R extends T, T> T walk(Function<Stream<StackWalker.StackFrame>, R>
> > function) //3
> > 
> > 
> > Under normal conditions (i.e. lambda parameter being passed to 'walk') I
> > think all these signatures are fundamentally equivalent; (2) and (3) seem
> > to have been designed for something like this:
> > 
> > Number n = walk(s -> new Integer(1));
> > 
> > That is, the function returns something that is more specific w.r.t. what
> > is expected in the return type of walk. But - in reality, if 'walk'
> > returns an R that is a subtype of T (as in the third signature), then walk
> > also returns a T (as R is a subtype of T), so the result value can be
> > passed where a T is expected.
> > 
> > The converse example:
> > 
> > Integer n1 = walk(s -> (Number)null);
> > 
> > Similarly fails on all three signatures.
> > 
> > 
> > More generally, with all such signatures, T will always receive:
> > 
> > * lower bound(s) (from the return value(s) of the lambda passed as a
> > parameter to the 'walk' method)
> > * one upper bound (from the target-type associated with the 'walk' call.
> > 
> > Under such conditions, the upper bound will always be disregarded in favor
> > of the lower bounds - meaning that instantiation of T will always be
> > driven by what's inside the lambda. Signature (3) mentions different
> > variables (R and T) but the end result is the same - as the bound says R
> > extends T - meaning that lower bounds of R are propagated to T - leading
> > to exactly the same situation.
> > 
> > 
> > In other words, I don't think there are obvious examples in which the
> > behavior of these three signatures will be significantly different - if
> > the return type of 'walk' would have been a generic type such as List<T>,
> > the situation would have been completely different - with with a 'naked'
> > T, I think the first signature is good enough, and the third is, in my
> > opinion, making things harder than what they need to be.
> > 
> > I think the second signature is not necessary, from a pure type-system
> > perspective; but I guess one could make an argument for it, but purely in
> > terms of consistency with other areas (after all, the second
> > type-parameter of a Function is in a covariant position).
> > 
> > I hope this helps.
> > 
> > Maurizio
> > 
> 

Reply via email to