如果您想要使用您所使用的幻像類型的運行時表示,則必須使用我們稱之爲單例的東西。它的構造函數的每個那些正是一個構造函數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中,以防您想要使用該代碼。
使用類型進行任何計算的最簡單方法是使用類型類。你想寫什麼函數? – user2407038