Haskell 类嵌套数据类型中的实例实现
我想知道如何为Haskell 类嵌套数据类型中的实例实现,haskell,Haskell,我想知道如何为Gmap数据类型实现instance Show,而不使用派生Show class GMapKey k where data GMap k :: * -> * empty :: GMap k v lookup :: k -> GMap k v -> Maybe v insert :: k -> v -> GMap k v -> GMap k v instance GMapKey Int where
Gmap
数据类型实现instance Show
,而不使用派生Show
class GMapKey k where
data GMap k :: * -> *
empty :: GMap k v
lookup :: k -> GMap k v -> Maybe v
insert :: k -> v -> GMap k v -> GMap k v
instance GMapKey Int where
data GMap Int v = GMapInt (Data.IntMap.IntMap v)
empty = GMapInt Data.IntMap.empty
lookup k (GMapInt m) = Data.IntMap.lookup k m
insert k v (GMapInt m) = GMapInt (Data.IntMap.insert k v m)
对于这一启动实施:
instance Show (GMap k v) where
show (GMapInt _) = undefined
编译器抛出:
* Couldn't match type `k' with `Int'
`k' is a rigid type variable bound by
the instance declaration
at /home/x/src/GMapAssoc.hs:27:10
Expected type: GMap k v
Actual type: GMap Int v
* In the pattern: GMapInt _
In an equation for `show': show (GMapInt _) = undefined
In the instance declaration for `Show (GMap k v)'
* Relevant bindings include
show :: GMap k v -> String
除了主要问题之外,我想了解为什么编译器在这种情况下不抱怨:
instance Show (GMap k v) where
show _ = undefined
GMapInt
构造函数是特定于GMap Int
的,因此除了Int
之外,您不能使用它为k
构造/解构GMap k
您可能需要此实例:
instance Show (GMap Int v) where
show (GMapInt _) = undefined
或者,如果Int
是唯一可以显示其映射的键类型(我觉得很奇怪)
后者的优点是,类型检查器在解析前不需要知道密钥是
Int
,例如显示空的(在第一种方法中,如果没有明确的类型签名,它将无法编译),相反,它可以利用密钥类型必须始终是Int
的知识。通常很有用,但在您的应用程序中可能不是正确的东西。如果不知道它的键类型,就无法对数据族(如GMap
)的数据构造函数进行模式匹配,也就是说,它不像GADT,因为它是开放的,所以不可能涵盖所有情况。因此,如果您想要实现一个通用的显示,您需要在不直接访问表示的情况下实现它。我有两个选择:
包罗万象
在进行了一些讨论之后,我能想到的最简单的方法是向类中添加一个方法,以便在实例中使用
class GMapKey k where
data GMap k :: * -> *
empty :: GMap k v
lookup :: k -> GMap k v -> Maybe v
insert :: k -> v -> GMap k v -> GMap k v
showGMap :: (Show v) => GMap k v -> String
instance (GMapKey k, Show v) => Show (GMap k v) where
show = showGMap
然后,您可以创建一个通用的catch-all实例
class GMapKey k where
data GMap k :: * -> *
empty :: GMap k v
lookup :: k -> GMap k v -> Maybe v
insert :: k -> v -> GMap k v -> GMap k v
showGMap :: (Show v) => GMap k v -> String
instance (GMapKey k, Show v) => Show (GMap k v) where
show = showGMap
这是一个有点不顺利,我会希望,但它不是太糟糕。我对这种方法的主要遗憾是,它排除了对实例的派生Show
,即
instance GMapKey Int where
data GMap Int v = GMapInt (Data.IntMap.IntMap v)
deriving (Show)
...
是非法的,因为它与catch-all实例重叠。如果类型变得复杂,这可能是一种痛苦。下一种方法没有这个问题
无论如何,现在我们有了一个实例,可以像往常一样使用它
example :: (GMapKey k, Show v) => GMap k v -> String
example gmap = show gmap
字典法
如果需要使用派生Show
,可以使用该软件包以更现代的方式进行
它还包括添加一个方法,但不是返回一个字符串
,而是返回一个完整的Show
字典
class GMapKey k where
data GMap k :: * -> *
empty :: GMap k v
lookup :: k -> GMap k v -> Maybe v
insert :: k -> v -> GMap k v -> GMap k v
showGMap :: (Show v) => Dict (Show (GMap k v))
您可以使用派生
进行实例化,而showGMap
实例总是相同的
instance GMapKey Int where
data GMap Int v = GMapInt (Data.IntMap.IntMap v)
deriving (Show)
...
showGMap = Dict
(您甚至可以使用来避免在实例中提到showGMap
)
不幸的是,catch-all实例仍将与此重叠,因此我们不会为GMap
s提供全局Show
实例。但是,我们可以在任何需要的地方使用
这是另一种恼人的方式。幸运的是,只有当我们需要一个通用的Show(GMap k v)
实例时,我们才需要这样做——如果我们已经知道k
是什么,那么我们派生的特定Show
实例就会工作
也许有一种方法可以两全其美?实例展示(GMap Int v)我也尝试过,但是ghc 8.0.2抛出异常非法实例声明来展示(GMap Int v)`它还说明了需要做些什么才能让它工作。根据你的建议,我实现了如下实例(k~Int,Show v)=>Show(GMap k v)其中Show(GMapInt k)=“GMapInt(“++(Show k)++”)”
但是我想实现一个通用的实例Show GMap
,不受Int
的限制。
example :: forall k v. (GMapKey k, Show v) => GMap k v -> String
example gmap = withDict @k @v (show gmap)