2010-05-13 18 views
8

¿Hay alguna forma de que pueda generalizar las definiciones de tipo aquí? Idealmente, me gustaría poder cambiar el tipo de 'testInput' y hacer una prueba de inferir correctamente el tipo en tiempo de compilación.C# Inferencia de argumento de tipo de método genérico

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; 
} 

Actualización: puedo especificar el tipo de la función pasó a componer, pero esto todavía especifica el tipo de la línea.

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

Un pequeño contexto; Estoy trabajando a través Wes Dyer The Marvel of Monads

+0

¿Puede proporcionar información más específica sobre lo que está tratando de lograr? Actualmente su función Compose está muy restringida para permitir la inferencia de tipo T de una lambda. Simplemente no puede obtener suficiente información del uso. – jeffora

+0

actualizó la pregunta. – CaptainCasey

Respuesta

3

Bueno, ya que estoy en un rollo por escupir texto esta noche voy a tener mi propia puñalada. Debo señalar que no soy un experto en el compilador de C#, no he leído las especificaciones (ninguna de ellas ... para nada), y aunque ese artículo que vinculó era realmente interesante, estaría mintiendo si dijera que era cualquier tipo de experto en eso tampoco (o incluso lo entendía todo al 100%).

Advertencias aparte, mi opinión sobre su pregunta es la siguiente:

¿Hay alguna manera de que puedo generalizar las definiciones de tipos de aquí?

Creo que la respuesta corta es no. Con la información proporcionada, simplemente no hay suficiente información para la parte de inferencia tipo del compilador de C# para inferir suficiente información del uso de las diversas variables.

Como demuestran las otras respuestas aquí, se puede simplificar. Puede usar @ Lee's IdentityFunc para permitir la inferencia de tipo con var identity. Sin embargo, incluso con esta adición, aún no es posible con su código de muestra inferir todas las variables de tipo Compose.

Imagínese la siguiente situación:

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; 
} 

y

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 
} 

Inicialmente Esto podría parecer como si test debe deducirse fácilmente a int. Sin embargo, el tipo de devolución de i.Compose solo se puede deducir después del hecho, a partir de su uso. El compilador de C# obviamente no permitirá esto.

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> 
} 

En ese ejemplo, al uso de c con a el compilador tendría que inferir retrospectivamente el tipo de retorno de la llamada a ser i.Compose<T, U, V>(n => n)Func<int, int>. Esto obviamente no es posible en el compilador de C#. Elimine la llamada c(a) y el compilador no tendría conocimiento del uso de c, lo que eliminaría cualquier posibilidad de inferir T (no es que pueda hacerlo de todos modos). Es posible que un sistema de inferencia de tipo más avanzado pueda hacer este tipo de inferencia en función del uso de una declaración genérica (posiblemente F # - otro tema sobre el que no soy experto).

Como Wes Dyer no proporciona un uso específico de ese ejemplo en particular, se desconoce si hay alguna otra magia que utilice para permitir el grado de inferencia de tipo que intenta lograr.

Más personas cualificadas como Eric Lippert podrían proporcionarle un nivel de detalle mucho mayor (y precisión/agudeza técnica). Leí una excelente respuesta que escribió aquí a una pregunta sobre la inferencia de tipo, pero no puedo encontrarla. Su blog tiene mucha información excelente. Podrías intentar contactarlo si estás interesado. Además, su respuesta a esta pregunta aquí analiza las mónadas (y finalmente enlaces al artículo de Wes Dyer) compre que podría estar interesado en leerlo: Monad in plain English? (For the OOP programmer with no FP background)

+2

En realidad, no tengo mucho que agregar a su respuesta. Como nota correctamente, sin el tipo en el parámetro lambda no tenemos suficiente información para deducir T del sitio de llamada de Compose. Y como nota, si pudiéramos diferir el análisis hasta el uso del resultado compuesto, entonces podríamos hacer más progresos, pero no hacemos eso. –

+0

Gracias por una excelente respuesta a la pregunta. Esta respuesta no tiene los votos positivos que claramente se merece. – CaptainCasey

2

Se puede escribir un método de extensión para volver a la función de identidad para un tipo:

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

(o simplemente return v => value;)

Su prueba se convierte entonces en

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

Lo más cercano que puedo obtener es escribir explícitamente el parámetro para n => n lambda:

var test = ((Func<int, int>)Identity).Compose((int n) => n)(testInput); 
1

No creo que pueda lograr su ideal; La inferencia tipo C# no funciona así.

Puede disfrutar de F #.

Cuestiones relacionadas