Я много раз читал это
Блоки, сгенерированные от F# или любого другого языка.NET, (почти) неразличимы.
Я тогда экспериментировал с F# и C# interop на.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)", то есть, параметры передаются как Кортеж.
Добавьте, что к чему-то, на чем я когда-то читал здесь ТАК (но не смог, находят, что это связывается с), по вопросу, где 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!)
Так, сложение этих трех цитат выше, можно прийти к заключению что:
(4, 5)
Add(Tuple<int, int>)
К моему удивлению это распечатало 9. Разве это не интересно?
Что действительно происходит здесь? Вышеупомянутые цитаты и это практические наблюдения, кажется, находятся в противоречии. Можно ли выровнять по ширине "обоснование" F#, и возможно указывающий на некоторые документы MSDN если возможный?
Спасибо!
(для добавления большей информации (из ответа Blindy))
Если Вы делаете:
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
.
Что происходит?!?
При передаче аргументов функциям из других библиотек .NET вы используете синтаксис вроде «.MethodName (parm1, parm2)», то есть параметры передаются как кортеж.
Это еще ужаснее. См. Описание разрешения перегрузки метода в спецификации языка .
По сути, в нем говорится, что аргумент при вызове метода на самом деле не кортеж. Это синтаксический кортеж, означающий разделенный запятыми список чего-либо, но круглые скобки являются частью синтаксиса вызова метода, как и запятые. Вот почему, например, o.M (a = 1, b = 2)
не является вызовом метода с кортежем из двух логических значений, а скорее с двумя именованными аргументами.
Итак, обычно каждый разделенный запятыми компонент просто отображается в отдельный аргумент. Следовательно, почему Add (1, 2)
вызывает перегрузку Add (int, int)
, а Add ((1, 2))
вызывает Add ( Кортеж
. Здесь нет двусмысленности.
Однако в вашем конкретном случае возникает особый случай:
Если нет именованных фактических аргументов и есть только один метод-кандидат в
M
, принимающий только один не- необязательный аргумент, тогда разложениеarg
в форму кортежа игнорируется, и есть один с именем фактическийarg
, который является самимarg
.
Итак, когда вы удалили все перегрузки, кроме кортежа, внезапно все, что было внутри круглых скобок, эффективно обрабатывается как конструктор кортежа при вызове. Но если вы, например, иметь две перегрузки, Add (int)
и Add (Tuple
, тогда вызов формы Add (1,2)
не будет Решаю вообще.
У меня сейчас не установлен F#, но мне кажется, что
MyClass.Add(4, 5) |> printf "%d"
напечатает 9, в то время как
MyClass.Add((4, 5)) |> printf "%d"
напечатает... 4 верно? Обратите внимание на двойные парантезы, внутреннюю пару, обозначающую кортеж, и внешнюю пару, обозначающую вызов функции.
Я не эксперт по F #, поэтому я могу немного ошибаться, но я предполагаю, что концепция кортежа в F # не коррелирует с системой BCL .Tuple
типа. Кортежи являются основным принципом F # и встроены в язык, но C #, VB.NET и большинство других языков .NET изначально не поддерживают кортежи. Поскольку кортежи могут быть полезны на этих языках, библиотека получает их поддержку.
Я бы продолжил свое предположение, сказав, что кортеж F # представлен в памяти почти так же, как параметры передаются методам в C # и других. То есть, по сути, они представляют собой массивы значений своих компонентов. Когда этот массив значений помещается в стек для вызова метода, это будет иметь тот же эффект, что и размещение каждого из его составляющих компонентов в стеке, точно так же, как когда этот метод вызывается из C #.
Итак, ваш второй пример создает кортеж F #, помещает его в стек, а затем вызывает перегрузку Add
, которая принимает типы, содержащиеся в кортеже.
Во всяком случае, это мое предположение. Предположительно, вы использовали F # больше, чем я, и вы могли бы получить больше информации. Вы также можете получить дополнительные подсказки, посмотрев на сгенерированный код Reflector .
Это просто волшебство компилятора.
let add a b = a+b
add
компилирует до add(a,b)
, облегчая вызов из C#. Тем не менее, F# программы все еще воспринимают его как add(b)
из-за атрибута в IL.
При вызове функций C# в F#, может помочь мысль о том, что функция C# имеет только один параметр - кортеж, элементы которого определяют правильную перегрузку. Таким образом, можно писать:
// MyClass.Add(5,3) = 8
let eight = (5,3) |> MyClass.Add