2

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!

Hobbes
  • 21
  • 2
  • 2
    I believe this has to do with the type inference of C# generics mixing with extension methods. The second method isn't actually generic, because you're constraining everything with the explicit types in the `this` reference. What you're seeing is exactly how generics are designed: they step in when explicit methods don't fit the bill. In your case, `double,double,string` Compiles will always hit the second and everything else will hit the generic method. – Joseph Yaduvanshi Apr 24 '10 at 05:15
  • 1
    @Jim: Why is this a comment and not an answer? – Adam Robinson Apr 24 '10 at 05:49
  • I think what offends you has to do with how overloading works. Switch the generic arguments to type object and you have two similar "ambiguous" methods. – rmac Apr 24 '10 at 06:29
  • @Adam: I didn't feel as though it 100% answered the question. I was going off of my own understanding of C# generics and C++/CLI templates. – Joseph Yaduvanshi Apr 24 '10 at 13:34

2 Answers2

6

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
  • 123,721
  • 27
  • 225
  • 246
0

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
  • 54,864
  • 2
  • 91
  • 133