2016-07-29 45 views
0

考慮以下幾點:在沒有專門使用構造函數的情況下獲取GADT風格聲明中的約束?

{-# LANGUAGE GADTs, GADTSyntax #-} 

data Test a where 
    Test :: Ord a => { first :: a, second :: a } -> Test a 

comp :: Test a -> Bool 
comp (Test fst snd) = fst < snd 

構造Test與一個Ord約束聲明。在comp,我已經明確採取Test構建的參數,這給Ord約束允許我使用<

現在,假設我想寫:

comp' :: Test a -> Bool 
comp' x = (first x) < (second x) 

使用投影功能,以獲得第一和第二元素。這是而不是沒問題,因爲我的參數x不是(必然)用Test構建的,所以沒有Ord約束。

所以,我的問題,是有辦法把參數作爲剛剛x但仍不知爲什麼從Test構造的Ord約束,而不必「解壓」或在Test構造模式匹配或添加約束我的功能?

至於爲什麼我想這一點,我有一個構造數據類型採取了許多價值觀,其中之一,我只需要在這個特殊的功能,所以拆包它使我的功能不必要的冗長:

myFunction :: Thing -> ... 
myFunction (Thing _ _ _ _ need _ _) ... 

與之相對

myFunction t = ... (need t) 
+2

您可以使用'myFunction t @ Thing {} = ...'。 – Alec

+0

'myfunction Thing {need = x}'這提供了'Ord'字典,而且'need'字段的值也已經解壓到'x'中。還有一個擴展可以讓你編寫'myFunction Thing {need}',並且在主體中你可以使用'need'這個名字來引用參數'need'字段的值。 – Bakuriu

+0

謝謝你們,不知道那種語法! – cemulate

回答

1

您可以定義它提取所有的約束從構造函數:

data Test a where 
    Test :: Ord a => { first :: a, second :: a } -> Test a 

openTest :: Test a -> (Ord a => r) -> r 
openTest Test{} x = x 

然後,您可以編寫

comp :: Test a -> Bool 
comp x = openTest x $ first x < second x 

但要注意的openTest的實施完全是微不足道的 - 這是打字少,只是內聯模式匹配:

comp' :: Test a -> Bool 
comp' [email protected]{} = first x < second x 

還要注意這一點(即Test{}語法)將與任何構造函數一起工作,即使它不是一個記錄構造函數。您的實際功能,可以簡單地表述爲

myFunction [email protected]{} = ... (need t) ... 

或等價

openThing :: Thing a -> (ThingConstraints a => r) -> r 
openThing Thing{} x = x 

myFunction t = ... (openThing $ need t) ... 

最後,您還可以定義一個函數來提取特定領域,以及適用於該領域的約束,這對於有很多約束條件的大型建設者非常有用:

first' :: Test a -> (Ord a => a -> r) -> r 
first' [email protected]{} x = x (first t) 
相關問題