C # ambiguity in Func + extension + lambdas methods
I tried to work my way through this article:
http://blogs.msdn.com/wesdyer/archive/2008/01/11/the-marvels-of-monads.aspx
... And something on page 1 made me uncomfortable. Specifically, I was trying to wrap my head around the Compose <> () function, and I wrote an example for myself. Consider the following two Func:
Func<double, double> addTenth = x => x + 0.10;
Func<double, string> toPercentString = x => (x * 100.0).ToString() + "%";
No problems! It is easy to see what these two people are doing.
Now, following the example from the article, you can write a generic extension method to create these functions, for example:
public static class ExtensionMethods
{
public static Func<TInput, TLastOutput> Compose<TInput, TFirstOutput, TLastOutput>(
this Func<TFirstOutput, TLastOutput> toPercentString,
Func<TInput, TFirstOutput> addTenth)
{
return input => toPercentString(addTenth(input));
}
}
Fine. So now you can say:
string x = toPercentString.Compose<double, double, string>(addTenth)(0.4);
And you get the line "50%"
So far so good.
But there is something ambiguous here. Let's say you write another extension method, so now you have two functions:
public static class ExtensionMethods
{
public static Func<TInput, TLastOutput> Compose<TInput, TFirstOutput, TLastOutput>(
this Func<TFirstOutput, TLastOutput> toPercentString,
Func<TInput, TFirstOutput> addTenth)
{
return input => toPercentString(addTenth(input));
}
public static Func<double, string> Compose<TInput, TFirstOutput, TLastOutput>(this
Func<double, string> toPercentString,
Func<double, double> addTenth)
{
return input => toPercentString(addTenth(input + 99999));
}
}
Therein lies the ambiguity. Do these two functions have overlapping signatures? Yes. Does it even compile? Yes. Which one is called? The second one (which clearly gives you the "wrong result") gets called. If you comment out any function, it still compiles, but you get different results.
Sounds like picky, but there is something that deeply offends my feelings here, and I cannot impose on it. Is it related to extension methods? Is this related to lambdas? Or is it related to the way Func <> allows you to parameterize the return type? I'm not sure.
I guess this is all covered in the spec somewhere, but I don't even know what to google to find.
Help!
a source to share
There is nothing ambiguous here. The second call will be called whenever it is an exact match. Whenever the match is not exact, you get the first function, because by default it will be an exact match for everything else.
If you create Func<double, string>
and the other is Func<double, double>
, call .Compose explicitly indicating <double, double, string>
that the compiler has enough information to determine that the second version will be an exact match, and therefore it is the one in use.
But consider this silly example:
Func<string, string> doubleString = s => s + s;
Func<DateTime, string> dateToString = date => date.ToString();
Func<DateTime, string> composedFunction = doubleString.Compose(dateToString);
Console.WriteLine(composedFunction(DateTime.Now));
Which version is being called? What's the result? First version, and the output is the date as a concatenated string for itself.
On the other hand, if you had a more realistic example using Func<double, string>
and Func<double, double>
and was not explicit with the Compose call, which version is called?
Func<double, string> toPercentString = d => d.ToString("0.0%");
Func<double, double> addTenth = d => d + 0.1;
Console.WriteLine(toPercentString.Compose(addTenth)(0.8));
The first, because the compiler detects it as an exact match with the second. Since I am not Eric Lippert or Jon Skeet, I will not even try to explain the connection to this.
static void DoSomething(float f, double d) { }
static void DoSomething(double d, float f) { }
...
DoSomething(1, 1);
This is ambiguous (and doesn't compile because of it).
a source to share
I don't see that this is for delegates, generics or extension methods; Your fundamental problem seems to be related to overloading (and in particular, resolving method overloading when there are multiple candidates). Consider:
public static class Test {
public static void Method(string s) {
Console.WriteLine("String version: " + s);
}
public static void Method(object o) {
Console.WriteLine("Object version: " + o.ToString());
}
public static void Main(string[] args) { Method("my string"); }
}
Q Main
, which method is being called? Is there any ambiguity? Compile-time overload resolution error and one method is determined to be more appropriate (method takes a string). As in your case, commenting out the first overload will cause the code to compile, but invoke a different overload. The same happens if the second method is defined as:
public static void Method<T>(T t) {
Console.WriteLine("Generic version: " + t.ToString());
}
While this is a candidate for overload resolution, an overload that takes a string is an exact match and is preferred.
a source to share