F# структурные Кортежи по сравнению с типами кортежей BCL

в F# вы можете определить следующим образом:

let first (x, y) = x

вы можете назвать это так:

first (1, 2)

вы также можете определить ту же функцию в терминах BCL Tuple тип:

let first (t:Tuple<_, _ >) = t.Item1

однако вы не можете вызвать его с помощью предыдущего синтаксиса, или вы получите следующую ошибку:

error FS0001: The type ''c * 'd' is not compatible with the type 'Tuple<'a,'b>'

вместо этого, вы должны сделать следующий:

first (Tuple<_,_>(1, 2))

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

почему это важно? Ну, в основном я хочу написать метод с перегрузками, поддерживающий кортеж произвольной длины. Это невозможно с синтаксическими кортежами F#, так как точное количество аргументов должно быть известно заранее. Тем не менее, это кажется возможным с помощью BCL Tuple типы, потому что те использовать TRest трюк, чтобы позволить кортежей произвольной длины. К сожалению, если я напишу свои перегрузки таким образом, они не будут работать с синтаксическими кортежами F#, что является конечной целью.

Итак, мой вопрос: почему синтаксические кортежи и кортежи BCL несовместимы? А также, есть ли примеры функций записи и / или методов, которые работают с кортежами произвольной длины в F#?

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

2 ответов


как обычно F# spec на помощь:

6.3.2 Выражения Кортежа!--16-->

выражение формы expr1, ..., exprn-это выражение кортежа. Например:

let three = (1,2,"3")
let blastoff = (10,9,8,7,6,5,4,3,2,1,0)

выражение имеет тип (ty1 * ... * tyn) для свежих типов ty1 ... tyn, и каждое отдельное выражение ei проверяется с использованием начального типа tyi.

типы кортежей и выражения преобразуются в приложения семейства типов библиотек F# с именем Система.Кортеж. Типы кортежей ty1 * ... * tyn переводятся следующим образом:

  • для n Tuple<ty1,...,tyn>.
  • для больших n типы кортежей являются сокращением для приложений дополнительного типа библиотеки F#System.Tuple<_> следующим образом:
  • для n = 8 разработанная форма Tuple<ty1,...,ty7,Tuple<ty8>>.
  • для 9 Tuple<ty1,...,ty7,tyB> где tyB-преобразованная форма типа (ty8 ... tyn).

выражения кортежей (expr1,..., exprn) переводятся следующим образом:

  • для N new Tuple<ty1,…,tyn>(expr1,...,exprn).
  • для n = 8 разработанная форма new Tuple<ty1,…,ty7,Tuple<ty8>>(expr1,...,expr7, new Tuple<ty8>(expr8).
  • для 9 new Tuple<ty1,...ty7,ty8n>(expr1,..., expr7, new ty8n(e8n) где ty8n-тип (ty8*...* tyn) и expr8n является разработанной формой выражения expr8,..., exprn.

если рассматривать как статические типы, типы кортежей отличаются от их закодированных форма. Однако закодированная форма значений и типов кортежей отображается в системе типов F# через типы среды выполнения. Например, typeof эквивалентен typeof<System.Tuple<int,int>> и (1,2) имеет тип времени выполнения System.Tuple<int,int>. Аналогично, (1,2,3,4,5,6,7,8,9) имеет тип выполнения Tuple<int,int,int,int,int,int,int,Tuple<int,int>>.

Примечание: до добавления кортежей в BCL в .NET 4.0 F# используется система.Тип кортежа, определенный в FSharp.Core dll

я думаю, единственный способ для вас иметь дело с кортежами произвольного размера-прибегнуть к построение и деконструкция тогда с функциями из Microsoft.FSharp.Reflection.FSharpType\FSharpValue


Я думаю, что ваше наблюдение о очень длинных кортежах частично отвечает на ваш вопрос - в F# вам разрешено иметь кортежи произвольной длины, поэтому совершенно нормально создать кортеж с 9 элементами:

let t = (1,1,1,1,1,1,1,1,1)

если вы посмотрите на тип времени выполнения, используя t.GetType() тогда это фактически скомпилировано во вложенный .NET tuple Tuple<int, int, int, int, int, int, int, Tuple<int, int>>.

Я не уверен, что это определенный ответ, но я думаю, что он показывает часть проблемы - если кортежи F# соответствуют кортежам .NET, то они либо нужно ограничить 8 элементами (чтобы соответствовать типам кортежей .NET), либо они будут "дырявой" абстракцией, а большие кортежи (молча) будут соответствовать некоторым вложенным типам кортежей.

Если вам нужна функция, которая работает на произвольном количестве элементов, то, возможно, было бы более целесообразно принять аргументы как список, а не Кортеж? Или вы можете написать функцию, которая работает на кортежах произвольного размера, используя отражение F# (в Microsoft.FSharp.Reflection)... Но я вижу, что это будет полезно для парсеров и других подходов может быть не так приятно.