Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Generic method type argument inference

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

like image 437
CaptainCasey Avatar asked May 13 '10 06:05

CaptainCasey


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr. Stroustroupe.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

Is C programming hard?

C is more difficult to learn than JavaScript, but it's a valuable skill to have because most programming languages are actually implemented in C. This is because C is a “machine-level” language. So learning it will teach you how a computer works and will actually make learning new languages in the future easier.


3 Answers

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: Monad in plain English? (For the OOP programmer with no FP background)

like image 52
jeffora Avatar answered Oct 11 '22 05:10

jeffora


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);
like image 36
Lee Avatar answered Oct 11 '22 04:10

Lee


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);
like image 20
Daniel Renshaw Avatar answered Oct 11 '22 05:10

Daniel Renshaw