F # - О параметрах, передаваемых в методы C # - они кортежи или что? - PullRequest
19 голосов
/ 08 января 2010

Я много раз читал, что

Сборки, сгенерированные из F # или любого другого языка .NET (почти) неразличимы.

Затем я экспериментировал с взаимодействием F # и C # в .NET 4 (бета 2). Я создал новое решение и проект на C # со следующим классом:

public class MyClass {
    public static int Add(int a, int b) { return a + b; }
}

Затем в проекте F #, после ссылки на проект C #, я попытался:

MyClsas.Add(4, 5) |> printfn "%d" // prints 9 (no kidding!)

Пока все хорошо. Затем мне пришло в голову другое предложение, которое я читал много раз (возможно, в разных книгах):

При передаче аргументов в функции из других библиотек .NET вы используете синтаксис, такой как ".MethodName (parm1, parm2)", то есть параметры передаются как кортеж.

Добавьте это к чему-то, что я когда-то читал здесь на SO (но не смог найти его для ссылки), на вопрос, где OP пытался создать использование типа [ 4, 5, 6 ] (когда он имел в виду [4; 5; 6]):

"Запятая - это оператор создания кортежа, для всего остального используйте точку с запятой."

Затем я изменил свой класс следующим образом:

public class MyClass {
    public static int Add(int a, int b) { return a + b; }
    public static int Add(Tuple<int, int> a) { return a.Item1; }
}

Теперь я попытался использовать его на F #:

MyClass.Add(4, 5) |> printf "%d" // prints ... (keep reading!)

Итак, сложив три цитаты выше, можно сделать вывод, что:

  • F # создаст кортеж, когда увидит (4, 5)
  • Тогда это вызовет перегрузку Add(Tuple<int, int>)
  • Так будет напечатано 4

К моему удивлению, напечатано 9 . Разве это не интересно?

Что на самом деле здесь происходит? Приведенные выше цитаты и это практические наблюдения, похоже, противоречат друг другу. Можете ли вы обосновать «рассуждения» F # и, если возможно, указать на некоторые документы MSDN?

Спасибо!

EDIT

(чтобы добавить больше информации (из ответа Блинди))

Если вы делаете:

MyClass.Add((4, 5)) |> printfn "%d" // prints 9

F # вызывает перегрузку Add(Tuple<int, int>).

Однако, если вы создадите еще один проект F # (таким образом, другую сборку) с этим:

namespace MyFSharpNamespace
type MyFShapClass = class
    static member Add x y = x + y
    end

Вы можете использовать его на C #, как это

public static void Main(string[] args) {
    MyFSharpNamespace.MyFSharpClass.Add(4, 5);
}

Пока все хорошо. Теперь, когда вы пытаетесь использовать его из F # (из другого проекта, другой сборки), вы должны сделать:

MyFSharpNamespace.MyFSharpClass.Add 4 5 |> printfn "%d"

Если вы передадите аргументы как (4, 5) F # не будет компилироваться, потому что Add это int -> int -> int, а не (int * int) -> int.

Что происходит?!?

Ответы [ 4 ]

19 голосов
/ 08 января 2010

При передаче аргументов в функции из других библиотек .NET вы используете синтаксис, такой как ".MethodName (parm1, parm2)", то есть параметры передаются как кортеж.

Это более отвратительно, чем это. См. Описание метода разрешения перегрузки метода из спецификации языка .

В сущности, это говорит о том, что аргумент в вызове метода на самом деле не кортеж. Это синтаксический кортеж, означающий разделенный запятыми список чего-либо, но скобки являются частью синтаксиса вызова метода, как и запятые. Вот почему, например, o.M(a=1, b=2) - это не вызов метода с кортежем из двух логических значений, а скорее два именованных аргумента.

Итак, обычно каждый компонент, разделенный запятыми, просто сопоставляется с отдельным аргументом. Следовательно, Add(1, 2) вызывает Add(int, int) перегрузку, а Add((1, 2)) вызывает Add(Tuple<int, int>). Здесь нет никакой двусмысленности.

Однако, особый случай, который включается для вашего конкретного случая, таков:

Если нет именованных фактических аргументов, и в M есть только один метод-кандидат, принимающий только один необязательный аргумент, то разложение arg в форму кортежа игнорируется, и есть один с именем фактический arg что само по себе arg.

Таким образом, когда вы удалили все перегрузки, за исключением одной кортежа, внезапно вся вещь в скобках фактически рассматривается как конструктор кортежа в вызове. Но если вы, например, есть две перегрузки, Add(int) и Add(Tuple<int,int>), тогда вызов вида Add(1,2) не будет разрешен вообще.

3 голосов
/ 08 января 2010

Это просто магия компилятора.

let add a b = a+b 

add компилируется до add(a,b), что упрощает вызов из C #. Однако программы F # все еще видят его как add a b из-за атрибута в IL.

При вызове функций C # в F # может оказаться полезным, чтобы функция C # имела только один параметр - кортеж, элементы которого определяют правильную перегрузку. Так что вы можете написать:

// MyClass.Add(5,3) = 8
let eight = (5,3) |> MyClass.Add
3 голосов
/ 08 января 2010

У меня сейчас не установлен F #, но мне кажется, что

MyClass.Add(4, 5) |> printf "%d"

будет печатать 9, тогда как

MyClass.Add((4, 5)) |> printf "%d"

напечатал бы .. 4 верно? Обратите внимание на двойные парантезы: внутренняя пара, обозначающая кортеж, и внешняя пара, обозначающая вызов функции.

0 голосов
/ 08 января 2010

Я не эксперт по F #, поэтому могу быть немного не в себе, но я предполагаю, что концепция F # кортежа не коррелирует с типом BCL System.Tuple. Кортежи являются основным принципом F # и встроены в язык, но C #, VB.NET и большинство других языков .NET изначально не поддерживают кортежи. Поскольку кортежи могут быть полезны на этих языках, библиотека получает их поддержку.

Я хотел бы далее предположить, что кортеж F # представлен в памяти почти так же, как параметры передаются в методы в C # и в друзья. То есть они по сути являются массивами значений своих компонентов. Когда этот массив значений помещается в стек для вызова метода, он будет иметь тот же эффект, что и перенос каждого из составляющих его компонентов в стек, точно так же, как когда этот метод вызывается из C #.

Итак, ваш второй пример создает кортеж F #, помещает его в стек, а затем вызывает перегрузку Add, которая принимает типы, содержащиеся в кортеже.

Во всяком случае, это мое предположение. Предположительно, используя F # больше, чем я, у вас может быть больше понимания этого. Вы также можете получить дополнительные подсказки, посмотрев на сгенерированный код Reflector .

...