为大型和类型编写Hashable实例

net*_*tom 1 haskell types generic-programming

我有一个很大的类型

data Value
= VNull
| VDouble !Double
| VSci !Scientific
| VInt !Int
| VText !Text
| VTexts ![Text]
| VByteString !BS.ByteString
| VUTCTime !UTCTime
-- This goes on for quite a few more lines
Run Code Online (Sandbox Code Playgroud)

我需要一个Hashable实例用于此数据类型.我当然可以手动输入实例,但幸运的是有一个基于泛型的hashWithSalt的默认实现.

不幸的是 - 据我所知 - 这需要任何可以在Value类型中"打包"的类型才能拥有Hashable实例.那么,UTCTime没有.

所以看起来我可以在两个"次优"解决方案之间做出选择:

  1. 手动输入Hashable实例.
  2. 编写Hashable UTCTime的孤立实例

我认为应该有第三种"最佳"方式:只为值构造函数编写一个无法自动执行的实现,即执行以下操作:

instance Hashable Value where
    hashWithSalt (VUTCTime t) = ... -- custom implementation
    hashWithSalt _ = ... -- use the default implementation
Run Code Online (Sandbox Code Playgroud)

当然可以更普遍地提出问题:如何在某些值构造函数的情况下重用现有的实例实现,同时在特定情况下拥有自己的实现,而无需为每个值构造函数编写样板文件.

lef*_*out 6

对于这种特殊情况,您应该使用hashable-time包,它在标准化的位置定义孤立实例.

总的来说,对于这种情况,我要么:

  • 将有问题的类型包装在a中newtype,这样您就可以在本地定义实例,而不会冒孤立实例的风险.
  • 只需编写孤立实例.如果其他人不太可能提供冲突的实例(即当类和类型都属于不太可能被其他人共同使用的模糊包时),那么这不是人们真正需要担心的事情(即使重复实例错误将在某个时刻发生,这很容易修复,这实际上是一件好事,删除了newtype会给出的冗余.
  • 将实例添加到最初来自的库中.如果任一类或类型从一个很普通的图书馆来了,那么它很可能是有意义的不常见的库定义实例.如果那是开源的,那么在那里添加实例并向作者发送拉取请求.