假設我有一個類,它為其成員型別的值宣告了一些建構式:
class C t where
fromInt :: Int -> t
fromString :: String -> t
進一步假設,我想使用這些多型建構式創建一堆值并將它們放入Mapfromcontainers包中。但重要的是,我希望這些值保持多型性,并推遲對其具體型別的決定,直到它們從地圖中提取出來。
newtype WrapC = WrapC (forall t . C t => t)
newtype MapC = MapC (Map String WrapC)
使用RankNTypes擴展我可以定義這樣一個映射,下面的定義見證了多型值確實可以從中提取的事實:
get :: C t => String -> MapC -> Maybe t
get key (MapC m) = fmap (\(WrapC t) -> t) $ Map.lookup key m
但是,當我想向映射中添加值時,我遇到了型別錯誤,因為 Haskell 顯然無法統一一些隱藏的型別變數。定義:
add :: C t => String -> t -> MapC -> MapC
add key val (MapC m) = MapC $ Map.insert key (WrapC val) m
無法編譯:
? Couldn't match expected type ‘t1’ with actual type ‘t’
‘t1’ is a rigid type variable bound by
a type expected by the context:
forall t1. C t1 => t1
at src/PolyMap.hs:21:53-55
‘t’ is a rigid type variable bound by
the type signature for:
add :: forall t. C t => String -> t -> MapC -> MapC
直覺上我猜這是隱藏在里面的型別變數WrapC無法統一。我不明白為什么需要這樣。或者我怎樣才能使這個例子作業......
uj5u.com熱心網友回復:
你只需要給你的函式一些實際上是多型的:
add :: String -> (forall t. C t => t) -> MapC -> MapC
但我可能會提出一些更愚蠢的建議。你能擺脫這個嗎?
type MapC = Map String (Either Int String)
get :: C t => String -> MapC -> Maybe t
get k m = either fromInt fromString <$> Map.lookup k m
不需要型別系統擴展。
uj5u.com熱心網友回復:
讓我通過解決最后一點來補充現有答案:
直覺上我猜這是隱藏在里面的型別變數
WrapC無法統一。我不明白為什么需要這樣。
您可以通過查看addand的型別簽名來了解問題所在get:
add :: C t => String -> t -> MapC -> MapC
get :: C t => String -> MapC -> Maybe t
您需要問的問題是“誰在為 選擇型別t?”。
的型別簽名add表示任何呼叫的人add都可以選擇t是任何東西(只要C t持有),并且該值將被插入到地圖中。
類似地,型別簽名get表示任何呼叫的人get都可以選擇t是任何東西(只要C t持有),并且該值將從映射中提取。
然而,這是行不通的:如果我們呼叫add選擇t=A,將值插入映射中,然后呼叫get從映射中提取相同的值選擇不同的值t=B?這相當于更改值的型別,因此必須禁止。
任何解決方案都必須讓兩個呼叫者t以某種方式達成一致。
一種解決方案是讓adddeposit 的呼叫者成為一個多型值,而不僅僅是t他們選擇的某些特定值。的呼叫者get仍然可以自由選擇任何t。
add :: String -> (forall t. C t => t) -> MapC -> MapC
get :: C t => String -> MapC -> Maybe t
另一種解決方案是讓呼叫者add自由選擇t他們喜歡的一個。然而,在這種情況下,呼叫者get必須適應任何這樣的選擇:結果型別將只被 存在量化而不是普遍量化。
add :: C t => String -> t -> MapC -> MapC
-- pseudo-code:
-- get :: String -> MapC -> (exists t. C t => Maybe t)
-- Since Haskell has no "exists", we need to encode it somehow, e.g.:
get :: String -> MapC -> SomeC
data SomeC = forall t . C t => SomeC (Maybe t)
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/474040.html
下一篇:從條件資料串列中獲取資料
