2014-02-19 42 views
6

在F#你可以如下定義first功能:F#結構的元組與BCL的元組類型

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。你可以看到我對元組有很多重載,但我不想將它們擴展到一些幻數。

回答

4

照例F#規格救援:

6.3.2元組表達式

形式表達式1的表達,...,exprn是元組的表達。例如:

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

表達具有用於新鮮類型TY1 ... TYN,並且每個單獨的表達EI使用初始型提格檢查的類型(TY1 * ... * TYN)。

元組類型和表達式被轉換爲名爲System.Tuple的F#庫類型系列的應用程序。的元組類型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)。

元組表達式(表達式1,...,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#用於FSharp.Core DLL中定義

我想唯一的方式爲您處理具有任意大小的元組System.Tuple類型訴諸然後用Microsoft.FSharp.Reflection.FSharpType\FSharpValue

+0

是的,'FSharpValue'這個東西不能用於我想要的東西,因爲它失去了靜態類型。我想我會提供一堆重載。 – luksan

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#反射(在Microsoft.FSharp.Reflection)編寫一個可以在任意大小的元組上工作的函數......但是我認爲這對分析器很有用,其他方法可能不會那麼好。

+0

構建和解構然後他們沒有9,10,11,12,13,14,15,16。那將會是笛卡爾式的關閉! – nicolas

+0

「Tuple'1''類型用於區分長元組和嵌套元組。例如,'(1,2,3,4,5,6,7,8,9)'映射到'Tuple >'而'(1,2,3,4, 5,6,7,(8,9))'映射到'Tuple >'。所以我不確定你提出的有關意外匹配的問題是否適用。 – luksan

+0

不幸的是,這個列表並不適用於我的案例,因爲這個想法能夠處理一組不同類型的問題。主要是我試圖避免提供大量的重載,以期望任何人可能想要與圖書館做什麼,但這似乎是不可避免的。 – luksan