Overload resolution simplification

Ali Ebrahimi ali.ebrahimi1781 at gmail.com
Fri Aug 23 06:10:59 PDT 2013


Hi,
My scheme can handle your overload related examples, except Ex.9.
Step 2 in proposal blocks Ex.9.

"Step 2: Scan lambda body for implicit nested lambdas
if does not exist any implicit nested lambdas goto step 3, otherwise ERROR."

Although, this constrain can be loosen as:

"Step 2: Scan lambda body for implicit nested lambdas
if does not exist any implicit nested lambdas as argument for
overloaded method calls goto step 3, otherwise ERROR."


Ex.8 requires reified generics which not supported in java yet.


Regards,
Ali Ebrahimi


On Fri, Aug 23, 2013 at 4:01 AM, Howard Lovatt <howard.lovatt at gmail.com>wrote:

> @Ali,
>
> Thanks for the correction re. char widening. The idea of using a type
> check on the lambda still stands though; it behaves like Java does.
>
> The disadvantage of type checking the lambda to resolve overloading is
> that it potential will take a long time. In practice plenty of languages
> have this issue: Haskell, ML, Scala, etc. in practice it isn't a big deal.
>
> The advantage of this type of checking is that you don't need to mangle
> method names to 'manually' resolve types. Mangled names have two
> disadvantages, they are not elegant and they are east to forget to use the
> mangled as opposed to the generic name.
>
> Therefore I would prefer a more sophisticated overload scheme and from
> looking at the examples that appears possible.
>
>  -- Howard.
>
> PS You scheme seems similar to me except that in the more difficult cases
> it generates an error, by design. It would be interesting to see how it
> behaves on the 9 examples I used and on any other pertinent examples.
>
> Sent from my iPad
>
> On 21/08/2013, at 7:14 PM, Ali Ebrahimi <ali.ebrahimi1781 at gmail.com>
> wrote:
>
> ops.
> I mean correct. just typo.
>
>
> On Wed, Aug 21, 2013 at 1:40 PM, Ali Ebrahimi <ali.ebrahimi1781 at gmail.com>wrote:
>
>> Hi,
>>
>>
>> On Wed, Aug 21, 2013 at 10:39 AM, Howard Lovatt <howard.lovatt at gmail.com>wrote:
>>
>>> Possible type inference algorithm using type checking of Lambdas (with
>>> examples below to show that it works well):
>>>
>>> I. Start with the outer-most method call, declaration, or assignment and
>>> work inwards and then left to right.
>>> II. For methods select a set of candidate methods based on known types,
>>> method name, and number of arguments and note the lambda candidate types.
>>> For declared types note the type. For assignments note the destination
>>> type.
>>> III. For each candidate type signature check the lambda including the
>>> return type against the candidate types and if the type check fails
>>> eliminate that candidate. This step whilst primarily eliminating
>>> candidates
>>> may add candidates because there may be multiple candidates for a given
>>> method/declaration/assignment based on multiple super-types of
>>> signatures.
>>> This stage, III., and the next, IV., may be usefully combined to make the
>>> process quicker. The process may also be carried out in parallel.
>>> IV. Choose from the remaining candidates the candidate method that is the
>>> best fit (least number of conversions - sum of conversion required for
>>> argument types and return type).
>>> V. If there are methods that are equally good fit issue an error giving
>>> the
>>> possible methods.
>>>
>>> The above algorithm is simple and doesn't involve complicated speculation
>>> about types, i.e. it works in a forward direction form signatures through
>>> lambda argument types to the lambda's return type. As shown below
>>> through a
>>> series of examples the algorithm works well and doesn't produce any
>>> surprises.
>>>
>>> In all cases below the unkown type of a single lambda argument, a, is A
>>> and
>>> for multiple arguments, a1, a2, ..., the types are A1, A2, ..., similarly
>>> an unknown return type is R and multiple are R1, R2, ...
>>>
>>> Maurizio's Examples (Lambda Experts Group email)
>>> ================================================
>>> class Mappers<T> {
>>>   IntSream map(IntFunction<T> if) {...}
>>>   <Z> Stream<Z> map(Function<T, Z> f) {...}
>>>   <U> IntStream map(Stream<U> s, IntFunction<U> if) {...}
>>>   <U, V> Stream<V> map(Stream<U> s, Function<U, V> f) {...}
>>> }
>>>
>>> Mappers<String> ms = ...;
>>> ms.map(a->1);                  // Ex. 1
>>> ms.map(a->'A');                // Ex. 2
>>> Stream<String> ss = ...;
>>> Mappers.map(ss, a->1);   // Ex. 3
>>> Mappers.map(ss, a->'A'); // Ex. 4
>>>
>>> Ex. 1: ms.map(a->1)
>>> -------------------
>>> 1. From the scope, function name, and number of arguments there are two
>>> possible functions map(IntFunction<String>) and map(Function<String, Z)
>>> and
>>> in both cases A = String and therefore A must be String.
>>> 2. Therefore (String a)->1 is either i. an IntFunction<String> (R = int)
>>> or
>>> ii. a Function<String, Z> (R = Object).
>>> 3. Type check option i., R = int, and this is OK since 1 is an int.
>>> 4. Type check option ii., R = Object, and this is OK since 1 could be
>>> autoboxed to an Integer.
>>> 5. Choose option i. because it doesn't require boxing.
>>>
>>> Ex. 2: ms.map(a->'A')
>>> ---------------------
>>> 1. From the scope, function name, and number of arguments there are two
>>> possible functions map(IntFunction<String>) and map(Function<String, Z)
>>> and
>>> in both cases A = String and therefore A must be String.
>>> 2. Therefore (String a)->'A' is either i. an IntFunction<String> (R =
>>> int)
>>> or ii. a Function<String, Z> (R = Object).
>>> 3. Type check option i., R = int, and this fails since 'A' is not
>>> automatically convertible to int.
>>>
>> This is not current. java can widen chars into ints from early days.
>>
>>
>> 4. Type check option ii., R = Object, and that is OK since 'A' could be
>>> autoboxed to a Character.
>>> 5. Therefore ii.
>>>
>> Widening wins over Boxing and Unboxing. So answer would be i.
>>
>>
>>
>>> Ex. 3: Mappers.map(ss, a->1)
>>> ----------------------------
>>> 1. From the scope, function name, and number of arguments there are two
>>> possible functions map(Stream<String>, IntFunction<String>) and
>>> map(Stream<String>, Function<String, Z) and in both cases U = A = String
>>> and therefore both U and A must be String.
>>> 2. Therefore (String a)->1 is either i. an IntFunction<String> (R = int)
>>> or
>>> ii. a Function<String, Z> (R = Object).
>>> 3. Type check option i, R = int, and this is OK since 1 is an int.
>>> 4. Type check option ii, R = Object, and this is OK since 1 could be
>>> autoboxed to an Integer.
>>> 5. Choose option i. because it doesn't require boxing.
>>>
>>> Ex. 4: Mappers.map(ss, a->'A')
>>> ------------------------------
>>> 1. From the scope, function name, and number of arguments there are two
>>> possible functions map(Stream<String>, IntFunction<String>) and
>>> map(Stream<String>, Function<String, Z) and in both cases U = A = String
>>> and therefore U and A must be String.
>>> 2. Therefore (String a)->'A' is either i. an IntFunction<String> (R =
>>> int)
>>> or ii. a Function<String, Z> (R = Object).
>>> 3. Type check option i., R = int, and this fails since 'A' is not
>>> automatically convertible to int.
>>>
>> So this is also not currect. See above.
>>
>> 4. Type check option ii., R = Object, and that is OK since 'A' could be
>>> autoboxed to a Character.
>>> 5. Therefore ii.
>>>
>> Answer: i ->  See above.
>>
>> May be you can read my proposal.
>>
>> http://mail.openjdk.java.net/pipermail/lambda-spec-observers/2013-August/000489.html
>>
>>
>> Ali Ebrahimi
>>
>>
>


More information about the lambda-spec-observers mailing list