使用類型級長度標註
所以這裏的交易。 Haskell確實有type-level natural numbers,您可以使用「幻像類型」對類型進行註釋。但是你這樣做,該類型將是這樣的:
data Z
data S n
data LAList x len = LAList [x] -- length-annotated list
然後你就可以添加一些功能建設爲方便:
lalist1 :: x -> LAList x (S Z)
lalist1 x = LAList [x]
lalist2 :: x -> x -> LAList x (S (S Z))
lalist2 x y = LAList [x, y]
-- ...
然後你已經有了比較通用的方法:
(~:) :: x -> LAList x n -> LAList x (S n)
x ~: LAList xs = LAList (x : xs)
infixr 5 ~:
nil :: LAList x Z
nil = LAList []
lahead :: LAList x (S n) -> x
lahead (LAList xs) = head xs
latail :: LAList x (S n) -> LAList x n
latail (LAList xs) = tail xs
但本身列表定義沒有任何這個,因爲它很複雜。您也許對Data.FixedList軟件包感興趣,但也有一些不同的方法。基本上,每種方法都會從一些沒有構造函數的數據類型開始看起來有點奇怪,但稍微有點後就開始看起來很正常。
您可能也能夠得到一個類型類讓所有的lalist1
的,lalist2
運營商上面可以用
class FixedLength t where
la :: t x -> LAList x n
替換,但你可能會需要-XTypeSynonymInstances
標誌要做到這一點,因爲你要像做
type Pair x = (x, x)
instance FixedLength Pair where
la :: Pair x -> LAList [x] (S (S Z))
la (a, b) = LAList [a, b]
(它是一種不匹配時,你去從(a, b)
到Pair a
)。
使用運行時檢查
你可以很容易地採取不同的方法和封裝所有的這是一個運行時錯誤或直接在您的代碼中的錯誤模式:
-- this may change if you change your definition of the Bunch type
features :: Entity -> [Feature]
features = fst
-- we also assume a runBunch :: [Entity] -> Something function
-- that you're trying to run on this Bunch.
allTheSame :: (Eq x) => [x] -> Bool
allTheSame (x : xs) = all (x ==) xs
allTheSame [] = True
permissiveBunch :: [Entity] -> Maybe Something
permissiveBunch es
| allTheSame (map (length . features) es) = Just (runBunch es)
| otherwise = Nothing
strictBunch :: [Entity] -> Something
strictBunch es
| allTheSame (map (length . features) es) = runBunch es
| otherwise = error ("runBunch requires all feature lists to be the same length; saw instead " ++ show (map (length . features) es))
那麼你runBunch
可以只是假設所有的長度都是相同的,並且明確地檢查了上面的內容。如果需要將特徵彼此相鄰配對,則可以利用Prelude中的zip :: [a] -> [b] -> [(a, b)]
函數解決模式匹配的奇怪問題。 (這裏的目標是由於runBunch' (x:xs) (y:ys)
和runBunch' [] []
的模式匹配而導致的算法錯誤,但是Haskell警告說有兩種模式在比賽中沒有考慮到。)
使用元組和類型類
最後一個辦法做到這一點這是兩者之間的妥協(但做了相當不錯的Haskell代碼)包括使實體參數化了對所有功能:
type Entity x = (x, Body)
然後包括它可以壓縮不同長度的不同實體在一起的功能:
class ZippableFeatures z where
fzip :: z -> z -> [(Feature, Feature)]
instance ZippableFeatures() where
fzip()() = []
instance ZippableFeatures Feature where
fzip f1 f2 = [(f1, f2)]
instance ZippableFeatures (Feature, Feature) where
fzip (a1, a2) (b1, b2) = [(a1, b1), (a2, b2)]
然後你可以使用元組的功能列表,只要它們不超過最大元組長度(這是我的GHC上的15)。如果你比這更大,當然,你總是可以定義自己的數據類型,但它不會像類型註釋列表一樣普遍。
如果你這樣做,對你的runBunch
類型簽名會簡單地樣子:
runBunch :: (ZippableFeatures z) => [Entity z] -> Something
當你對事物有錯號碼的功能,你會得到編譯器錯誤,它不能統一運行(a,b,c)的類型(a,b)。
在編譯時已知「實體」中的「特徵」數量是否已知?無論哪種方式,這聽起來很像[依賴類型](https://www.fpcomplete.com/user/konn/prove-your-haskell-for-great-safety/dependent-types-in-haskell),但我對這個話題不太熟悉。 – Zeta 2014-10-16 17:52:14