您定義
class TListGen ixs (g :: * -> *) where
genTList :: Proxy ixs -> g ix -> TList ixs g
但是這意味着該函數的調用者可以選擇什麼來實例化所有類型的變量,所以還特別如何實例ix
。
因此,例如,
genTList (Proxy :: Proxy '[Int, String]) (Just False)
將是這個函數的類型調用正確,選擇g
是Maybe
和ix
是Bool
。但這不可能是正確的。我們需要傳遞一個足夠多態的東西,它至少適用於類型級列表中出現的所有元素,或者更好,可用於ix
的任何可能選擇。這是一個等級2多態類型所實現的:
class TListGen ixs (g :: * -> *) where
genTList :: Proxy ixs -> (forall ix . g ix) -> TList ixs g
這需要RankNTypes
語言擴展。
現在調用者只能傳遞參數類型爲g
的多態的函數。所以通過Just False
將不再工作,但通過Nothing
會沒事的。
這些案例的定義原則上可以,但實際上您可以刪除OVERLAPS
附註,甚至是代理參數,因爲沒有任何重疊,並且可以從結果類型推斷出ixs
,因爲它作爲參數出現到TList
數據類型:
class TListGen ixs (g :: * -> *) where
genTList :: (forall ix . g ix) -> TList ixs g
instance TListGen '[] g where
genTList _ = TNil
instance TListGen ixs g => TListGen (ix ': ixs) g where
genTList g = g :-: genTList g
現在我們可以嘗試使用它:
GHCi> genTList Nothing :: TList '[ Int, String ] Maybe
不幸的是,這將導致一個錯誤,因爲沒有Show
instan CE:
• No instance for (Show (TList '[Int, String] Maybe))
arising from a use of ‘print’
• In a stmt of an interactive GHCi command: print it
定義Show
實例TList
是可能的,但有點棘手。
我不知道這是否是主要的鍛鍊,但如果你確定只重用代碼,那麼這一切都是在generics-sop封裝。
你TList
被稱爲NP
(與論點翻轉順序爲),你的genTList
被稱爲pure_NP
,所以你可以寫
GHCi> import Generics.SOP.NP
GHCi> pure_NP Nothing :: NP Maybe '[ Int, String ]
Nothing :* (Nothing :* Nil)
謝謝!這是半的鍛鍊確實,但主要是我想有更少的依賴關係,並保持它的簡單。 –