LJC Lambdas Hackday
Rémi Forax
forax at univ-mlv.fr
Mon Jun 4 08:09:48 PDT 2012
On 06/04/2012 04:26 PM, Maurizio Cimadamore wrote:
> Hi David,
> The kind of local technique you describe in your email has been on the
> table for quite some time. The only thing about it that makes us
> skeptical is its brittleness - i.e. if one day we would need to add
> another overload for 'into' - all clients using diamond will be broken.
>
> Maurizio
I agree but it's not very different of adding an overload with a parameter
that have a type more specific that the one of the existing method.
It's not a source compatible change.
Said in another way, overloads with different SAMs is a straight path to
hell.
IDE should warn about that.
Rémi
>
> On 04/06/12 04:39, David Conrad wrote:
>> On Tue, 29 May 2012 14:35:16 +0100, Maurizio Cimadamore<
>> maurizio.cimadamore at oracle.com> wrote:
>>
>>> On 29/05/12 14:30, Richard Warburton wrote:
>>>> 1. Target typing for diamond operators being passed into methods
>>>> doesn't seem to work quite right at the moment.
>>> Thanks for the feedback - the rationale for the current compiler
>>> behaviour is explained in more details here:
>>>
>>> http://mail.openjdk.java.net/pipermail/lambda-dev/2012-May/004951.html
>>>
>>> Maurizio
>>>
>>>
>> And on Tue, 29 May 2012 10:11:18 -0400, Brian Goetz<brian.goetz at oracle.com
>>> wrote:
>>> I wish we could consider this a simple bug, but as Maurizio points out,
>>> this is a pretty big leap from local type inference towards more global
>>> type inference. We're evaluating our options here, but if we could have
>>> fixed this already, we would have :(
>>>
>>>
>> I think you can do better. I'll explain below, but first I want to pull in
>> the
>> content from Maurizio's link, above:
>>
>>
>> On Sun May 27 04:03:41 PDT 2012, Maurizio Cimadamore<
>> maurizio.cimadamore at oracle.com> wrote:
>>> m(List<String> ls) { ... }
>>>
>>> m(new ArrayList<>)
>>>
>>> If you have inference variables on both ends, you have an inference
>>> cycle. Now, since Java overload resolution is a function of argument
>>> types only (the target type of the assignment is only considered at a
>>> later stage), we are not supposed to look into that during overload
>>> resolution.
>>>
>>> It's also a problem of whether we want inference to be more global vs.
>>> keeping it local - your case might look reasonable enough - but then
>>> what about:
>>>
>>> List<String> ls = m(m(m(m(ArrayList<>))));
>>>
>>> Making inference more powerful will make those problems disappear, that
>>> will have other side-effects - for instance the fact that you will get
>>> strange error messages in unexpected places (as the compiler 'delays'
>>> the time at which inference is performed to wait for some constraints).
>> Okay, so there are two ways to fix this. One is to use more powerful,
>> global type inference, but that has undesirable effects. But look back
>> at the previous paragraph: "overload resolution is a function of argument
>> types only". Overload resolution? I've tripped over the same thing that
>> Richard Warburton did, and it amounts to this:
>>
>> public static void main(String[] args) {
>> Set<Integer> set = Arrays.iterable(args)
>> .map(Integer::new)
>> .into(new TreeSet<>());
>> }
>>
>> So to complete overload resolution on Iterable::into the compiler needs
>> to know the argument types, and assumes TreeSet<Object> because
>> it doesn't yet know what method it's dealing with, and so cannot infer
>> anything better than Object for the diamond syntax.
>>
>> Okay, now go open up Iterable.java and count the number of into
>> methods. :) (Forgive me for being overly cute; couldn't resist.)
>>
>> If the compiler short-circuits overload resolution in the case (and only
>> the case) where there is one-and-only-one method by that name, it
>> can handle this or even m(m(m(m(new ArrayList<>())*, but not:
>>
>>> List<String> ls = m(m(m(m(ArrayList<>))));
>> In this case, the type is known only by the target of the assignment.
>> In the other cases, it's known from the receiver of the ::into call.
>>
>> * Assuming there is only one m() method, that is. If there's more than
>> one, you just get the exact same error you get now.
>>
>> Obviously you know better than I whether this can work, but it
>> would make ::into a whole lot nicer without requiring much more
>> smarts from type inferencing. In particular, it doesn't require any
>> global inferencing, only knowledge about the receiver, which it
>> must have to even begin overload resolution.
>>
>> David
>>
>> P.S. ->
>>
>> Finally, on Tue, 29 May 2012 15:51:32 +0100, Richard Warburton<
>> richard.warburton at gmail.com> wrote:
>>
>>> I think it might well be useful to have sum operate over types
>>> generally. So if you have a Stream<T> and can provide a Mapper<T,T>
>>> then you can sumBy generally. Obviously there are a variety of
>>> mathematical structures that you might want to sum that aren't
>>> primitives - for example a vector. I'd probably expect that many
>>> domain objects are the kind of things that you want to sum over as
>>> well in some cases. Presumably any domain object that's a commutative
>>> monoid is safe for this kind of stuff?
>>>
>>> In these cases I think its safe for the signature of sumBy to be:
>>>
>>> <T> T sumBy(Iterable<T> values, Mapper<T,T> summer)
>>>
>>> And people can provide their definition of the summer function,
>>> instead of explicitly restricting the type of<T> to things that you
>>> can already syntactically do t1 + t2 on.
>>>
>>>
>> You know, it would be really, really nice one day if arithmetic
>> operators worked on BigInteger, or any ? extends Number.
>>
>> But that's a Coin for another time....
>>
>
More information about the lambda-dev
mailing list