F#结构元组与BCL元组类型

时间:2014-02-19 02:48:17

标签: f# base-class-library

在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#中对任意长度的元组进行操作?

特定的应用程序处理我正在编写的基于类型推断的二进制解析库。您可以查看代码here。你可以看到我对元组的许多重载,但我不想将它们扩展到一些神奇的数字。

2 个答案:

答案 0 :(得分:5)

我认为你对非常长的元组的观察部分回答了你的问题 - 在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#reflection(在Microsoft.FSharp.Reflection中)编写一个适用于任意大小元组的函数......但我发现这对解析器很有用,其他方法可能不太好。

答案 1 :(得分:4)

像往常一样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),并且使用初始类型tyi检查每个单独的表达式ei。

元组类型和表达式被转换为名为System.Tuple的一系列F#库类型的应用程序。元组类型ty1 * ... * tyn翻译如下:

  • 对于n <= 7,精心制作的表格为Tuple<ty1,...,tyn>
  • 对于较大的n,元组类型是附加F#库类型System.Tuple<_>的应用的简写,如下所示:
  • 对于n = 8,精心制作的表格为Tuple<ty1,...,ty7,Tuple<ty8>>
  • 对于9&lt; = 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&lt; = 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>>

注意:在.NET 4.0中将元组添加到BCL之前F#使用FSharp.Core dll中定义的System.Tuple类型

我想你处理任意大小的元组的唯一方法是使用Microsoft.FSharp.Reflection.FSharpType\FSharpValue

中的函数来构造和解构