"Select" call ambiguity

20 views
Skip to first unread message

Don Reba

unread,
Oct 6, 2008, 1:35:09 PM10/6/08
to Nemerle Forum
This was posted by VladD2 at RSDN:


I think that someone has already encountered the following problem.
When LINQ extension functions are used, the Nemerle compiler emits an
ambiguity error. Here is a classic example:
def ary = array[1.1, 2.2];

_ = ary.Select(x => x);

This code is perfectly compilable in C# 3.0, but the Nemerle compiler
gives the following error message:

Error: typing fails on ambiguity between overloads:
Error: Posible overload: method System.Linq.Enumerable.Select(source :
System.Collections.Generic.IEnumerable[TSource.282], selector :
System.Func[TSource.282, int, TResult.283]) :
System.Collections.Generic.IEnumerable[TResult.283]
Error: Posible overload: method System.Linq.Enumerable.Select(source :
System.Collections.Generic.IEnumerable[TSource.284], selector :
System.Func[TSource.284, TResult.285]) :
System.Collections.Generic.IEnumerable[TResult.285]

Unfortunately, the error message only says that there is an ambiguity,
but does not explain how it gets it.
At first glance, this is a compiler error.
At second glance (armed with a debugger), the compiler is perfectly
correct.
The thing is that Nemerle can see the type parameter not only as a
simple type, but also as a tuple.
One of the overloaded Select methods accepts as a parameter a lambda
with the signature:

Func[TSource, int, TResult]

corresponding to the type:

(TSource * int) -> TResult

in Nemerle.

When we give the method a lambda like this:

x => x

the compiler reasonably assumes that type "x" can also be a "TSource *
int".
In other words, it sees the lambda as if it was written like this:

fun (x : TSource * int) : TResult { x }

But, on the other hand, when attempting to type this very lambda as a
parameter of the second overloaded Select method, it enters it like
this:

fun (x : TSource * int) : TResult { x }

In this manner, both versions become equally viable, and the compiler
cannot read one of them.
This result in the ambiguity error.

You can easily verify my conclusions by modifying the code slightly.
The following easily passes compilation:

def ary = array[1.1, 2.2];
_ = ary.Select(x => x + .0);

In this case the lambda is inferred as double -> double.

The following example also compiles perfectly well:

def ary = array[1.1, 2.2];
_ = ary.Select((x, _) => x);

Of course, the type of lambda is then double * int -> double.
The second parameter is not used (it is replaced by the placeholder
symbol "_").
Here is how you can make the compiler understand that the parameter is
passed a tuple:

def ary = array[1.1, 2.2];
_ = ary.Select((x : _ * _) => x);

In this case the lambda is inferred as double * int -> double * int.

Unfortunately, the code

def ary = array[1.1, 2.2];
_ = ary.Select((x : _) => x);


creates an ambiguity, since "_" cannot be seen as a tuple. In essence,
this syntax is equivalent to the one in which the type is not
specified at all.

Unfortunately, automatic tuple conversion leads to less convenient
usage of linq functions.
Linq allows use of your own functions instead of those defined by MS,
since it works with the query pattern, not specific methods.
One could right a custom version of the class System.Linq.Enumerable:

using System.Console;
using IEnumerable = System.Collections.Generic.IEnumerable;
using Func = System.Func;

module MyEnumerable
{
// Реализованы только два метода: Select и Where.
public Select[TSource, TResult](this source : IEnumerable[TSource],
selector : Func[TSource, TResult]) : IEnumerable[TResult]
{
System.Linq.Enumerable.Select(source, selector)
}

public Where[TSource](this source : IEnumerable[TSource],
predicate : Func[TSource, bool]) : IEnumerable[TSource]
{
System.Linq.Enumerable.Where(source, predicate)
}
}

module Program
{
Main() : void
{
def ary = array[1.1, 2.2];
def r = ary.Where(x => x > 2.0).Select(x => x);
WriteLine($"..$r");
_ = ReadLine();
}
}

This example compiles perfectly and outputs "2.2" to the console.

This way, it would be rational to create the namespace Nemerle.Linq,
in which to place classes containing extension methods created after
the above example.
Reply all
Reply to author
Forward
0 new messages