【问题标题】:Associated data families and overlapping instances关联数据族和重叠实例
【发布时间】:2018-02-22 16:03:31
【问题描述】:

我想要一个“通用”地图数据结构,它可以通过提供自定义实例来有效地专门化,就像在 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 定义“默认”实例吗?

【问题讨论】:

  • 数据族必须不能重叠。否则,类型安全不再适用……不过这个想法很有趣。
  • 为什么会这样?是否存在与关联类型重叠不安全的情况,而仅重叠方法仍然安全? I.o.w.什么时候重叠类型族比重叠类型类“更不安全”?
  • 重叠关联的 type 系列很好,但不是 data 系列。多态性因重叠的数据系列而崩溃。 Here's a sketch.
  • (继续讨论该要点)

标签: haskell ghc type-families associated-types


【解决方案1】:

放弃重叠实例的一种解决方案是使用默认关联的单射类型族(非常多)。我还为默认的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

我宁愿看到一个使用重叠实例的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-05
    • 1970-01-01
    • 1970-01-01
    • 2018-10-14
    相关资源
    最近更新 更多