假设我有一个类,它为其成员类型的值声明了一些构造函数:
class C t where
fromInt :: Int -> t
fromString :: String -> t
进一步假设,我想使用这些多态性构造函数创建一堆值,然后将它们放入中>容器
软件包。但重要的是,我希望这些值保持多态性并将其决定在其混凝土类型上延迟,直到从地图中提取它们为止。
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
中的类型变量,无法统一。我不明白的是为什么需要成为。或我该如何使这个示例起作用…
Suppose I've got a class, which declares some constructors for values of its member types:
class C t where
fromInt :: Int -> t
fromString :: String -> t
Further suppose, I want to create a bunch of values using these polymorphic constructors and put them in a Map
from containers
package. But importantly, I want the values to remain polymorphic and deferr the decision on their concrete type until they are extracted from the map.
newtype WrapC = WrapC (forall t . C t => t)
newtype MapC = MapC (Map String WrapC)
Using RankNTypes
extension I can define such a map and the following definition witnesses the fact that polymorphic values indeed can be extracted from it:
get :: C t => String -> MapC -> Maybe t
get key (MapC m) = fmap (\(WrapC t) -> t) $ Map.lookup key m
However, when I want to add a value to the map, I'm facing a type error, because Haskell apparently cannot unify some hidden type variables. The definition:
add :: C t => String -> t -> MapC -> MapC
add key val (MapC m) = MapC $ Map.insert key (WrapC val) m
fails to compile with:
• 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
Intuitively I'm guessing that's the type variable hidden inside WrapC
that cannot be unified. What I don't understand is why it needs to be. Or how can I make this example work…
发布评论
评论(2)
您只需要将您的功能交付实际上是多态性的:
但是我可能会提出一些笨拙的东西。你能摆脱这个吗?
无需类型的系统扩展。
You just need to hand your function something actually polymorphic:
But I would propose something even dumber, maybe. Can you get away with this?
No type system extensions needed.
让我通过解决最后一点来补充现有答案:
您可以通过查看
添加
和get
的类型签名来查看问题:您需要问的问题是“谁在选择
t的类型
?”。添加
的类型签名说,谁调用add
可以选择t
是任何事物(只要c c t
持有),该值将插入地图。同样,
get
的类型签名说,谁调用get
可以选择t
是任何事物(只要C T 保留),该值将从地图中提取。
但是,这是无法使用的:如果我们称
添加
选择t = a
,请在地图中插入值,然后调用get
从地图中提取相同的值选择不同的t = b
?这将等于更改值的类型,因此必须不允许。任何解决方案都必须使两个呼叫者以某种方式就
t
达成一致。一种解决方案是使
的呼叫者添加
存放一个多态值,而不仅仅是它们选择的某些特定t
的值。get
的呼叫者仍然可以免费选择任何t
。另一个解决方案是保持
添加
的呼叫者免费选择他们喜欢的一个t
。但是,在这种情况下,get
的呼叫者必须适应任何此类选择:结果类型仅是生存量化的,而不是普遍的 。Let me complement the existing answer by addressing the last point:
You can see what is going wrong by looking at the type signatures for
add
andget
:The question you need to ask is "who is choosing the type for
t
?".The type signature for
add
says that whoever callsadd
can chooset
to be anything (as long asC t
holds), and that value will be inserted in the map.Similarly, the type signature for
get
says that whoever callsget
can chooset
to be anything (as long asC t
holds), and that value will be extracted from the map.This, however, can not work: what if we call
add
choosingt=A
, insert the value in a map, and then callget
to extract the same value from the map choosing a differentt=B
? This would amount to changing the type of the value, hence must be disallowed.Any solution must make the two callers agree on
t
in some way.One solution is to make the caller of
add
deposit a polymorphic value, not just a value for some specifict
they chose. The caller ofget
remains free to choose anyt
.Another solution is to keep the caller of
add
free to choose only onet
of their liking. However, in such case, the caller ofget
must adapt to any such choice: the result type will only be existentially quantified instead of universally quantified.