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
)... Но я вижу, что это будет полезно для парсеров и других подходов может быть не так приятно.