views:

226

answers:

3

I've been trying to make 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. In particular, I was trying to wrap my head around the Compose<>() function, and I wrote an example for myself. Consider the following two Func's:

Func<double, double> addTenth = x => x + 0.10;
Func<double, string> toPercentString = x => (x * 100.0).ToString() + "%";

No problem! It's easy to understand what these two do.

Now, following the example from the article, you can write a generic extension method to compose these functions, like so:

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 string "50%"

So far, so good.

But there's 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));
 }
}

Herein is the ambiguity. Don't these two function have overlapping signatures? Yes. Does this even compile? Yes. Which one get's called? The second one (which clearly gives you the "wrong" result) gets called. If you comment out either function, it still compiles, but you get different results.

It seems like nitpicking, but there's something that deeply offends my sensibilities here, and I can't put my finger on it. Does it have to do with extension methods? Does it have to do with lambdas? Or does it have to do with how Func<> allows you to parameterize the return type? I'm not sure.

I'm guessing that this is all addressed somewhere in the spec, but I don't even know what to Google to find this.

Help!

+4  A: 

There's nothing ambiguous here. The second one will get called whenever it is an exact match. Whenever the match is not exact, you get the first function, because by default it is going to be an exact match for everything else.

If you create a Func<double, string>, and another that is Func<double, double>, call .Compose while explicitly specifying <double, double, string>, the compiler has enough information to determine that the second version is going to be an exact match and therefore it is the one used.

But consider this foolish 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 gets called? What's the result? The first version, and the output is the date as a string concatenated to itself.

On the other hand, if you had a more realistic example using Func<double, string> and Func<double, double> and weren't as explicit with the call to Compose, which version gets 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 one, because the compiler determines it to be the exact match versus the second. Since I'm not Eric Lippert or Jon Skeet, I'll not even attempt to explain the binding on that one.


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).

Anthony Pegram
A: 

Thank you for all the answers. Although the method of resolving this possible ambiguity makes sense, imagine if the static class was partial and that these two methods appeared in different files. There could be a lot of confusion. Again, this won't happen very often, but it makes me uncomfortable.

Hobbes
A: 

I don't see what this has to do with delegates, generics, or extension methods; your fundamental problem seems to be with overloading (and specifically, method overload resolution 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"); }

}

In Main, which method gets called? Is there ambiguity? Overload resolution kicks during compilation and one method is determined to be a better fit (the method taking a string). Just as in your case, commenting out the first overload will result in code which compiles but calls the other overload. The same thing happens if the second method is defined as:

    public static void Method<T>(T t) { 
        Console.WriteLine("Generic version: " + t.ToString()); 
    }

Although this is a candidate for overload resolution, the overload taking a string is an exact match and is preferred.

kvb