Haskell 中的多态值映射

Sve*_*mir 4 polymorphism haskell typeclass rank-n-types

假设我有一个类,它为其成员类型的值声明了一些构造函数:

\n
class C t where\n  fromInt :: Int -> t\n  fromString :: String -> t\n
Run Code Online (Sandbox Code Playgroud)\n

进一步假设,我想使用这些多态构造函数创建一堆值并将它们放入Mapfromcontainers包中。但重要的是,我希望这些值保持多态性,并推迟对其具体类型的决定,直到从地图中提取它们为止。

\n
newtype WrapC = WrapC (forall t . C t => t)\n\nnewtype MapC = MapC (Map String WrapC)\n
Run Code Online (Sandbox Code Playgroud)\n

使用RankNTypes扩展,我可以定义这样一个映射,以下定义见证了确实可以从中提取多态值的事实:

\n
get :: C t => String -> MapC -> Maybe t\nget key (MapC m) = fmap (\\(WrapC t) -> t) $ Map.lookup key m\n
Run Code Online (Sandbox Code Playgroud)\n

然而,当我想向映射添加一个值时,我遇到了类型错误,因为 Haskell 显然无法统一一些隐藏的类型变量。定义:

\n
add :: C t => String -> t -> MapC -> MapC\nadd key val (MapC m) = MapC $ Map.insert key (WrapC val) m\n
Run Code Online (Sandbox Code Playgroud)\n

无法编译:

\n
\xe2\x80\xa2 Couldn't match expected type \xe2\x80\x98t1\xe2\x80\x99 with actual type \xe2\x80\x98t\xe2\x80\x99\n  \xe2\x80\x98t1\xe2\x80\x99 is a rigid type variable bound by\n    a type expected by the context:\n      forall t1. C t1 => t1\n    at src/PolyMap.hs:21:53-55\n  \xe2\x80\x98t\xe2\x80\x99 is a rigid type variable bound by\n    the type signature for:\n      add :: forall t. C t => String -> t -> MapC -> MapC\n
Run Code Online (Sandbox Code Playgroud)\n

直觉上我猜测这是隐藏在里面的类型变量WrapC无法统一。我不明白的是为什么需要这样。或者我怎样才能让这个例子工作\xe2\x80\xa6

\n

Dan*_*ner 9

你只需要给你的函数一些实际上多态的东西:

add :: String -> (forall t. C t => t) -> MapC -> MapC
Run Code Online (Sandbox Code Playgroud)

但也许我会提出一些更愚蠢的建议。你能摆脱这个吗?

type MapC = Map String (Either Int String)

get :: C t => String -> MapC -> Maybe t
get k m = either fromInt fromString <$> Map.lookup k m
Run Code Online (Sandbox Code Playgroud)

不需要类型系统扩展。