关联数据族和重叠实例
Associated data families and overlapping instances
我想要一个 'generic' 地图数据结构,它可以通过提供自定义实例来有效地专门化,很像 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
定义一个 'default' 实例?
放弃重叠实例的一个解决方案是使用默认的关联单射类型族(相当多)。我还为默认 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
我宁愿看到一个使用重叠实例的解决方案。
我想要一个 'generic' 地图数据结构,它可以通过提供自定义实例来有效地专门化,很像 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
定义一个 'default' 实例?
放弃重叠实例的一个解决方案是使用默认的关联单射类型族(相当多)。我还为默认 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
我宁愿看到一个使用重叠实例的解决方案。