2011-12-28 69 views
6

我翻譯下面的Haskell代碼OCaml的:記錄類型沒有類型構造函數?

data NFA q s = NFA 
{ intialState :: q 
, isAccepting :: q -> Bool 
, transition :: q -> s -> [q] 
} 

起初我嘗試了很直譯:

type ('q,'s) nfa = NFA of { initialState: 'q; 
          isAccepting: 'q -> bool; 
          transition: 'q -> 's -> 'q list } 

...當然這給出了一個語法錯誤,因爲類型構造的一部分,「NFA of」是不允許的。它必須是:

type ('q,'s) nfa = { initialState: 'q; 
         isAccepting: 'q -> bool; 
         transition: 'q -> 's -> 'q list } 

這讓我想知道爲什麼這樣。爲什麼你不能像記錄類型一樣爲記錄類型設置類型構造函數(如下所示)?

type ('q, 's) dfa = NFA of ('q * ('q->bool) * ('q -> 's -> 'q list)) 

回答

15

你爲什麼要想要記錄類型的類型構造函數,除非因爲那是你在Haskell中的習慣?

在Haskell中,記錄不完全是一流的構造:它們更像是元組頂部的語法糖。您可以定義記錄字段的名稱,將它們用作訪問者,並進行部分記錄更新,但可以通過純元組中的位置進行訪問。因此,構造函數名稱在去除之後需要告訴另一個記錄:如果沒有構造函數名稱,那麼具有不同字段名稱但字段類型相同的兩個記錄會解析爲相同的類型,這將是一件壞事。

在OCaml中,記錄是一種原始概念,它們有自己的身份。因此,它們不需要頭構造函數來將它們與相同字段類型的元組或記錄區分開來。我不明白你爲什麼要添加一個頭構造函數,因爲這更詳細,沒有提供更多信息或幫助表達。

爲什麼你不能像記錄類型一樣使用類型構造函數(如下)?

小心!在你顯示的例子中沒有元組,只有一個具有多個參數的和類型。 Foo of bar * baz不是Foo of (bar * baz)相同:前者的構造函數有兩個參數,後者的構造函數只有一個參數,它是一個元組。這種區分是由於性能原因(在內存中,兩個參數與構造函數標籤一起打包,而元組創建間接指針)完成的。使用元組而不是多參數略微更靈活:您可以匹配Foo (x, y) -> ...Foo p -> ...,後者不適用於多參數構造函數。

在元組和記錄之間沒有不對稱,因爲它們都沒有在和類型構造中的特殊狀態,它只是任意元素構造函數的總和。也就是說,使用元組作爲構造函數的參數類型更容易,因爲元組類型不必聲明爲使用。例如。有些人要求的能力寫的

type foo = 
    | Foo of { x : int; y : int } 
    | Bar of { z : foo list } 

而不是當前

type foo = Foo of foo_t | Bar of bar_t 
and foo_t = { x : int; y : int } 
and bar_t = { z : foo list } 

您的要求是這個問題的一個特定的(而且不是很有趣)的情況下。但是,即使使用這種簡寫語法,構造函數和數據之間仍會存在一個間接指針,這使得這種風格對於注重性能的程序而言不具吸引力 - 可以使用命名爲的構造函數參數。 PS:我並不是說Haskell選擇desugaring記錄到元組是一件壞事。通過將一個功能轉換爲另一個功能,可以減少概念的冗餘/重疊。這就是說,我個人認爲將元組解析成記錄會更自然(使用數字字段名稱,例如Oz)。在編程語言設計中,通常沒有「好」和「壞」選擇,只有不同的折衷。

+0

「你爲什麼想要一個類型構造函數的記錄類型」 - 你說得對,它們通常沒有用,除了可能有一種情況:包含記錄的和類型:type somerecs = REC1 {a:.. ; b:..} | REC2的{c:..; d ..}等等。但正如你所說,這很容易解決。 「 – aneccodeal 2011-12-29 00:06:12

+0

」在你演示的例子中沒有元組,只有一個帶有多個參數的總和類型,bar * baz的Foo與(bar * baz)的Foo不一樣。「謝謝,那是一個提示! – aneccodeal 2011-12-29 00:09:50

+0

很少的事情:1.當你說記錄是一個「原始概念」和「有自己的身份」時,我認爲你的意思是記錄是OCaml中的一個特例,與SML中的記錄不同,它們是一流的。 2.從程序員的角度來看,誰是內存佈局細節的(不言而喻)不可知的,這裏*是*記錄和元組之間的不對稱性w.r.t構造函數參數。你的例子演示了它。 3.記錄作爲參數的構造函數與具有命名參數的構造函數不同。 – 2015-07-17 07:59:59

2

你不能擁有它,因爲語言不支持它。它實際上可以很容易地融入到類型系統或數據表示中,但它在編譯器中會是一個小小的額外複雜因素,並且尚未完成。所以是的,你必須選擇命名構造函數和命名參數。

請注意,唱片標籤名稱與特定類型綁定,例如, {initialState=q}('q, 's) nfa類型的模式;您不能有用地重用不同類型的標籤名稱。因此,當類型有多個構造函數時,命名構造函數纔是真正有用的;那麼,如果你有很多參數給構造函數,你可能更喜歡爲參數定義一個單獨的類型。

我相信這個功能有一個補丁,但我不知道它是否是最新的OCaml版本或任何東西,它需要任何使用您的代碼的人都擁有該補丁。

相關問題