2015-12-21 43 views
0

我用Haskell聲明瞭一個像這樣的幻像類型。檢索隱藏類型的幻像類型

newtype Length (a::UnitLength) b = Length b deriving (Eq,Show) 
data UnitLength = Meter 
       | KiloMeter 
       | Miles 
       deriving (Eq,Show) 

現在,我想寫一些函數來使用這種類型。但是我沒有碰巧看到並使用隱藏類型。

是否有可能檢索幻像類型Length的隱藏類型a以執行測試,模式匹配....?

+2

使用類型進行任何計算的最簡單方法是使用類型類。你想寫什麼函數? – user2407038

回答

4

如果您想要使用您所使用的幻像類型的運行時表示,則必須使用我們稱之爲單例的東西。它的構造函數的每個那些正是一個構造函數UnitLength及其類型說正是其構造我們正在考慮:

data SUnitLength (a :: UnitLength) where 
    SMeter  :: SUnitLength Meter 
    SKiloMeter :: SUnitLength KiloMeter 
    SMiles  :: SUnitLength Miles 

現在,你有這個你可以,例如寫一個顯示功能選擇取決於權單位縮寫在幻影參數:

display :: Show b => SUnitLength a -> Length a b -> String 
display sa l = show (payload l) ++ 
    case sa of 
    SKiloMeter -> "km" 
    _   -> "m" 

現在,這並不真正符合您的需求:該參數a是在類型Length a b可用,但我們卻不知何故仍然有能力製造手工見證。這很煩人。避免這個問題的一種方法是定義一個爲我們做這項工作的類型類。 CUnitLength a告訴我們,提供了一個值爲Length a b的值,我們可以得到形狀爲a的見證人SUnitLength a了。

class CUnitLength (a :: UnitLength) where 
    getUnit :: Length a b -> SUnitLength a 

很容易讓我們寫的CUnitLength的各種UnitLength構造函數實例:getUnit甚至可以忽略它的參數!

instance CUnitLength Meter where 
    getUnit _ = SMeter 

instance CUnitLength KiloMeter where 
    getUnit _ = SKiloMeter 

instance CUnitLength Miles where 
    getUnit _ = SMiles 

那麼爲什麼要打擾getUnit的論點呢?那麼,如果我們刪除它,getUnit需要以某種方式神奇地猜測它想要描述哪個a。有時可以根據呼叫站點的預期類型推斷出a,但有時不是。擁有Length a b參數可以保證所有呼叫都是明確的。我們總是可以恢復簡單getUnit'反正:

getUnit' :: CUnitLength a => SUnitLength a 
getUnit' = getUnit (undefined :: Length a()) 

這使我們具有相同的作用display但不要求額外的參數,最後的定義display'

display' :: (CUnitLength a, Show b) => Length a b -> String 
display' = display getUnit' 

我已經把一切(包括LANGUAGE擴展名和​​以從Length a b中提取b的定義)在self-contained gist中,以防您想要使用該代碼。

+0

「SUnitLength」是一個GADT,因此關閉,這一事實似乎有點尷尬。此外,我不明白你爲什麼想在單元上進行模式匹配。像數據家族這樣的東西會更合適嗎? – dfeuer

+0

'UnitLength'已經關閉了,所以除了提供的三個單元外,不可能使用'Length'。 – gallais

+0

是的,但不能換成'*'或其他東西? – dfeuer