Структурные кортежи F# и типы кортежей BCL

В F# вы можете определить функцию first следующим образом:

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#?

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


person luksan    schedule 19.02.2014    source источник


Ответы (2)


как обычно спецификация F# на помощь:

6.3.2 Выражения кортежа

Выражение вида 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.

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

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

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

  • Для n ‹= 7 разработана форма new Tuple<ty1,…,tyn>(expr1,...,exprn).
  • Для n = 8 разработана форма new Tuple<ty1,…,ty7,Tuple<ty8>>(expr1,...,expr7, new Tuple<ty8>(expr8).
  • Для 9 ‹= n расширенная форма 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# использовал тип System.Tuple, определенный в FSharp.Core dll.

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

person desco    schedule 19.02.2014
comment
Да, вещь FSharpValue не будет работать для того, что я хочу, потому что она теряет статическую типизацию. Думаю, я просто предоставлю кучу перегрузок. - person luksan; 20.02.2014

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

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

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

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

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

person Tomas Petricek    schedule 19.02.2014
comment
жаль, что у них нет 9,10,11,12,13,14,15,16. это было бы достаточно декартово замкнуто! - person nicolas; 19.02.2014
comment
Тип Tuple`1 используется для различения длинных кортежей и вложенных кортежей. Например, (1,2,3,4,5,6,7,8,9) соответствует Tuple<int, ..., Tuple<int, int>>, тогда как (1,2,3,4,5,6,7,(8,9)) соответствует Tuple<int, ..., Tuple<Tuple<int, int>>. Поэтому я не уверен, что вопрос о случайном совпадении, который вы поднимаете, применим. - person luksan; 20.02.2014
comment
К сожалению, в моем случае список не работает, поскольку идея заключалась в том, чтобы иметь возможность работать с группой разрозненных типов. В основном я просто пытался избежать огромного количества перегрузок в ожидании того, что кто-то может захотеть сделать с библиотекой, но это кажется неизбежным. - person luksan; 20.02.2014