views:

350

answers:

4

Is there any way that I can generalise the type definitions here? Ideally, I'd like to be able to change the type of 'testInput' and have test correctly infer the type at compile time.

public static void Run()
{
    var testInput = 3;
    var test = ((Func<int, int>) Identity).Compose<int,int,int>(n => n)(testInput);
    Console.WriteLine(test);
}

public static Func<T, V> Compose<T, U, V>(this Func<U, V> f, Func<T, U> g)
{
    return x => f(g(x));
}

public static T Identity<T> (this T value)
{
    return value;
}

Update: I can specify the type of the function passed into Compose but this still specifies the type in the line.

public static void Run()
{
    var testInput = 3;
    var identity = (Func<int, int>) Identity;
    var test = identity.Compose((int n) => n)(testInput);
    Console.WriteLine(test);
}

A little context; I'm working through Wes Dyer's The Marvel of Monads

+2  A: 

You could write an extension method to return the Identity function for a type:

public static Func<T, T> IdentityFunc<T>(this T value)
{
    return (Func<T, T>)Identity;
}

(or just return v => value;)

Your test then becomes

var testInput = 3; 
var identity = testInput.IdentityFunc();
test = identity.Compose((int n) => n)(testInput);
Lee
+1  A: 

The closest I can get is to explicitly type the parameter for the n => n lambda:

var test = ((Func<int, int>)Identity).Compose((int n) => n)(testInput);
Daniel Renshaw
+1  A: 

I don't think you can achieve your ideal; C# type inference does not work like that.

You might enjoy F#.

Brian
+3  A: 

Well seeing as I'm on a roll for spewing out text tonight I'll have my own stab at it. I should note that I am no expert on the C# compiler, I haven't read the specification (any of them... for anything), and although that article you linked was really interesting, I'd be lying if I said I was any sort of expert on that either (or even understood it all 100%).

Caveats aside, my take on your question is this:

Is there any way that I can generalise the type definitions here?

I think the short answer is no. With the information provided, there is simply not enough information for the type inference part of C#'s compiler to infer enough information from the usage of the various variables.

As the other answers here demonstrate, it can be simplified. You can use @Lee's IdentityFunc to allow for type inference with var identity. However, even with this addition, it is still not possible with your sample code to infer all the type variables of Compose.

Imagine the following situation:

public static Func<T, V> Compose<T, U, V>(this Func<U, V> f, Func<T, U> g)
{
    return x => f(g(x));
}

public static T Identity<T> (this T value)
{
    return value;
}

public static Func<T, T> IdentityFunc<T>(this T value)
{
    return (Func<T, T>)Identity;
}

and

public static void Run()
{
    var a = 3; // a is int
    var i = a.IdentityFunc(); // i is Func<int, int>;
    var test = i.Compose(n => n)(a) // test is expected to be int
}

Initially this might appear as if test should easily be inferred to int. However, the return type of i.Compose can only be inferred after the fact, from its usage. The C# compiler obviously won't allow this.

public static void Run()
{
    var a = 3; // a is int
    var i = a.IdentityFunc(); // i is Func<int, int>;
    var c = i.Compose(n => n) // c is Func<T, int> - T cannot be resolved without knowledge of n
    var test = c(a); // ideally have type inference infer c (Func<T, int>) as Func<int, int>
}

In that example, upon usage of c with a the compiler would have to retrospectively infer the return type of the call to i.Compose<T, U, V>(n => n) to be Func<int, int>. This is obviously not possible in the C# compiler. Take away the call c(a) and the compiler would have no knowledge of the usage of c, which would remove any possibility of inferring T (not that it can anyway). It is possible a more advanced type inference system would be able to do this sort of inferring based on the usage of a generic return (possibly F# - another topic I'm no expert on).

As Wes Dyer doesn't provide specific usage of that particular example, it's unknown whether there is some other magic he uses to allow for the degree of type inference you're trying to achieve.

More qualified people like Eric Lippert would be able to provide you with a much greater level of detail (and technical accuracy / acuity). I read a great response he wrote on here to a question on type inference, but I can't find it. His blog has lots of great information. You could try contacting him if you're interested. Also, his answer to this question here discusses monads (and ultimately links back to Wes Dyer's article) buy you might be interested in reading it: http://stackoverflow.com/questions/2704652/monad-in-plain-english-for-the-oop-programmer-with-no-fp-background/2704795#2704795

jeffora
Actually I don't have much to add to your answer. As you correctly note, without the type on the lambda parameter we do not have enough information to deduce T from the call site of Compose. And as you note, if we could defer analysis until the use of the composed result then we could make more progress, but we don't do that.
Eric Lippert
Thank you for an excellent answer to the question. This answer doesn't have the up-votes it clearly deserves.
CaptainCasey