违反mapKeysMonotonic前提条件

时间:2015-08-11 09:52:40

标签: json haskell dictionary

我想发送一些JSON,它将包含从(Database.Persist.Sql)键到值的映射。显而易见的解决方案是使用Map (Key x) v,但这不是ToJSON的实例。

一些替代方案是:

  • 不要使用JSON,使用更好的内容(例如edn
  • 不返回Map k v,返回Map Text v(但此处我丢失了某些类型信息)
  • type JsonKey = TextMap JsonKey v,但这不是更好
  • 定义我自己的ToJSON实例。

后者似乎是需要较少变化的那个,而更清晰的变化。

另外,请注意我只需要ToJSON个实例,而不是FromJSON,所以我不需要整个往返。

所以,这就是我想写的:

import Database.Persist.Sql (Key)
instance ToJSON (ToJSON v => (Map (Key a) v))

假设Key有一个友好的Show实例(它没有,但这只是一个细节)......我正要写:

instance ToJSON (ToJSON v => (Map (Key a) v)) where
  toJSON m = toJSON $ mapKeysMonotonic show m

但是我立即意识到这很糟糕(键就像整数一样):

> 9 < 10
True
> "9" < "10"
False

这违反了mapKeysMonotonic前提条件。

现在,与此同时,我可以使用mapKeys ......但我试着想一想,违反这个的风险是什么? mapKeys显然效率较低。我理解这可能是过早优化的原因。

但是我仍然想知道这可能会破坏的方式......或者如果它对我的有限用例确实安全的话。我唯一关心的是没有价值丢失。我不在乎他们的订单。

现在,this is instance (ToJSON v) => ToJSON (M.Map String v)。它会将Data.Map转换为Data.HashMap

依赖mapHashKeyVals ...使用Data.Map.foldrWithKey

我还想过使用HashMap (Key x) v,以避免N*log(N)计算或mapKeysMonotonic。但我仍然定义了ToJSON实例,显然mapKeys没有HashMap(可以理解的是,因为这需要重新计算所有哈希值,但我&#39} ;我不确定这实际上是否比mapKeys

现在,我尝试了这个简单的例子:

> mapKeysMonotonic show $ fromList [(10,"a"), (9, "b"), (99, "c"), (100, "d")]
fromList [("9","b"),("10","a"),("99","c"),("100","d")]

并且由于show为每个不同的String返回了不同的Int,显然没有值会丢失。地图现在可能不平衡......但后果是什么?我猜uniondifferenceintersection可能行为不端......但是遍历和折叠呢?在所有情况下,这些仍然会保留所有元素吗?

PS:我刚刚意识到另一个可能的解决方案可能是定义instance ToJSON (ToJSON v => [(Key x, v)])并将其转换为内部的Map ......但我想这将是一个重叠的实例

2 个答案:

答案 0 :(得分:10)

您可以使用Map.showTree检查地图的二叉树结构。 mapKeysMonotonic函数保持相同的树顺序,只是替换节点,因此任何涉及搜索树的访问者都会得到错误的结果。

不搜索的遍历很可能会起作用,但使用这样的映射会违反数据结构的逻辑,这会导致混淆和错误。

要定义ToJSON实例,您不需要构建中间地图;您可以使用foldrWithKey直接构建Value。请注意,Aeson中的Object构造函数只是HashMap Text Value。折叠本身是O(n)并且插入HashMap是(实际上)恒定的时间。像这样:

instance ToJSON v => ToJSON (Map (Key a) v) where
  toJSON = Object . Map.foldrWithKey f mempty
    where f = HashMap.insert . Text.pack . show

答案 1 :(得分:0)

如果您无法保证该功能是单调的,您可以使用

mapKeys :: Ord k2 => (k1 -> k2) -> Map k1 a -> Map k2 a

这是O(n*log n),因此它比mapKeysMonotonicO(n)。但是,它只有log n因子较慢,所以它可能已经足够了。

在任何情况下,我都不会违反Map不变量:您可能会导致元素从地图中消失或被复制。