我喜欢'泛型'通过提供自定义实例来有效地专业化的地图数据结构,就像在the GHC manual section on type families中一样。
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module MapKey where
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
class MapKey k where
data MMap k :: * -> *
instance {-# OVERLAPPING #-} MapKey () where
newtype MMap () v = UnitMap (Maybe v)
instance {-# OVERLAPPABLE #-} Ord k => MapKey k where
newtype MMap k v = OrdMap (Map k v)
可悲的是,这不起作用。 GHC(8.2.1)抱怨:
Conflicting family instance declarations:
MMap () = UnitMap (Maybe v)
MMap = OrdMap (Map k v)
|
14 | newtype MMap () v = UnitMap (Maybe v)
|
是否有一些语言扩展允许这个?
否则,还有另一种方法可以让用户轻松定义“默认值”。 Ord
的实例?
答案 0 :(得分:2)
放弃重叠实例的一个解决方案是使用默认关联的内射类型族(非常满口)。我还为默认的MMap
同义词附加了一些默认实现的方法:
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
module MapKey where
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
class MapKey k where
type MMap k v = r | r -> k v
type MMap k v = Map k v
empty :: MMap k v
default empty :: (MMap k v ~ Map k v) => MMap k v
empty = Map.empty
insert :: k -> v -> MMap k v -> MMap k v
default insert :: (MMap k v ~ Map k v, Ord k) => k -> v -> MMap k v -> MMap k v
insert = Map.insert
lookupLE :: k -> MMap k v -> [(k, v)]
default lookupLE :: (MMap k v ~ Map k v, Ord k) => k -> MMap k v -> [(k, v)]
lookupLE k m =
case Map.lookupLE k m of
Nothing -> []
Just e -> [e]
instance MapKey () where
type MMap () v = Maybe v
empty = Nothing
insert _ v _ = Just v
lookupLE _ m =
case m of
Nothing -> []
(Just v) -> [((), v)]
这意味着客户端代码仍然需要定义样板孤立实例,如
instance MapKey Int
我宁愿看到使用重叠实例的解决方案。