C # Общий вывод типа аргумента метода - PullRequest
8 голосов
/ 13 мая 2010

Есть ли способ обобщить определения типов здесь? В идеале я хотел бы иметь возможность изменить тип testInput и правильно проверить тип во время компиляции.

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;
}
* * 1003 Обновление: Я могу указать тип функции, передаваемой в Compose, но это все равно указывает тип в строке.
public static void Run()
{
    var testInput = 3;
    var identity = (Func<int, int>) Identity;
    var test = identity.Compose((int n) => n)(testInput);
    Console.WriteLine(test);
}

Немного контекста; Я работаю через Уэса Дайера Чудо Монад

Ответы [ 4 ]

3 голосов
/ 13 мая 2010

Хорошо, учитывая, что я сегодня нахожусь в процессе изложения текста, у меня будет свой удар. Я должен отметить, что я не эксперт по компилятору C #, я не читал спецификацию (любую из них ... ни для чего), и хотя эта статья, на которую вы ссылались, была действительно интересной, я бы соврал, если бы сказал в этом тоже был какой-то эксперт (или даже все понял на 100%).

Предостерегаю, мой вопрос на ваш вопрос таков:

Есть ли способ обобщить определения типов здесь?

Я думаю, что короткий ответ - нет. С предоставленной информацией просто недостаточно информации для части вывода типа компилятора C #, чтобы вывести достаточно информации из использования различных переменных.

Как показывают другие ответы, это можно упростить. Вы можете использовать @ Lee IdentityFunc, чтобы разрешить вывод типа с var identity. Однако даже с этим дополнением все еще невозможно с вашим примером кода вывести все переменные типа Compose.

Представьте себе следующую ситуацию:

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

и

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
}

Первоначально это может выглядеть так, как будто test должно быть легко выведено на int. Однако тип возвращаемого значения i.Compose может быть определен только после факта, из его использования. Компилятор C #, очевидно, не допустит этого.

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

В этом примере при использовании c с a компилятору придется ретроспективно выводить тип возврата вызова i.Compose<T, U, V>(n => n), равный Func<int, int>. Это явно невозможно в компиляторе C #. Уберите вызов c(a), и компилятор не будет знать об использовании c, что исключит любую возможность сделать вывод T (но это не так). Вполне возможно, что более продвинутая система вывода типов сможет сделать вывод такого типа на основе использования обобщенного возврата (возможно, F # - еще одна тема, в которой я не разбираюсь).

Поскольку Уэс Дайер не дает конкретного использования этого конкретного примера, неизвестно, использует ли он какую-то другую магию, чтобы учесть степень вывода типов, которую вы пытаетесь достичь.

Более квалифицированные люди, такие как Эрик Липперт , смогут предоставить вам гораздо более высокий уровень детализации (а также техническую точность / остроту). Я прочитал отличный ответ, который он написал здесь, на вопрос о выводе типов, но я не могу его найти. Его блог содержит много полезной информации. Вы можете попробовать связаться с ним, если вам интересно. Кроме того, в его ответе на этот вопрос обсуждаются монады (и, в конечном счете, ссылки на статью Уэса Дайера). Возможно, вам будет интересно прочесть ее: Монада на простом английском? (Для программиста ООП без фона FP)

2 голосов
/ 13 мая 2010

Вы можете написать метод расширения для возврата функции Identity для типа:

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

(или просто return v => value;)

Ваш тест становится

var testInput = 3; 
var identity = testInput.IdentityFunc();
test = identity.Compose((int n) => n)(testInput);
1 голос
/ 13 мая 2010

Я не думаю, что вы можете достичь своего идеала; Вывод типа C # не работает так.

Возможно, вам понравится F #.

1 голос
/ 13 мая 2010

Самое близкое, что я могу получить - это явно ввести параметр для n => n лямбда:

var test = ((Func<int, int>)Identity).Compose((int n) => n)(testInput);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...