我的总和类型很大
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
我需要此数据类型的Hashable实例。我当然可以手动键入实例,但是幸运的是,基于泛型的hashWithSalt有一个默认实现。
不幸的是,据我所知,这要求可以“打包”在Value类型内的任何类型都具有Hashable实例。好吧,UTCTime没有一个。
所以看起来我可以在两个“次优”解决方案之间进行选择:
我认为应该是第三种“最佳”方法:只为不可能自动执行的值构造函数编写实现,即执行以下操作:
instance Hashable Value where
hashWithSalt (VUTCTime t) = ... -- custom implementation
hashWithSalt _ = ... -- use the default implementation
当然可以更普遍地问这个问题:在某些值构造函数的情况下,如何重用现有的实例实现,而在特定情况下具有自己的实现,而不必为每个值构造函数编写样板。
答案 0 :(得分:6)
对于这种特殊情况,您应该只使用hashable-time package,它在标准化的位置定义了孤立实例。
通常对于这种情况,我会:
newtype
中,这样您就可以在本地定义实例,而不必担心出现孤立实例麻烦。newtype
会带来的冗余)。答案 1 :(得分:3)
我希望添加一个孤立实例。无论如何,您可以避免以下情况。
定义此辅助类型
data ValueNotTime
= VNull
| VDouble !Double
| VSci !Scientific
| VInt !Int
| VText !Text
| VTexts ![Text]
| VByteString !BS.ByteString
并自动派生Hashable。然后,写一个同构
iso :: Value -> Either ValueNotTime UTCTime
osi :: Either ValueNotTime UTCTime -> Value
以明显的方式。然后,
instance Hashable Value where
hashWithSalt v = case iso v of
Left valueNoTime -> use derived implementation (hashWithSalt valueNoTime)
Right utcTime -> use custom implementation
答案 2 :(得分:2)
这似乎是从以下位置获取孤立实例的好地方:https://hackage.haskell.org/package/hashable-time
如果导出了一个通用实现,例如genericHashWithSalt
(但目前还没有https://github.com/tibbe/hashable/issues/148),则可以这样做
data Value_ utctime
= ...
| VUTCTime utctime
deriving (Generic, Functor)
type Value = Value_ UtcTime
instance Hashable Value where
hashWithSalt s (VUTCTime t) = (my custom implementation) s t
hashWithSalt s v = genericHashWithSalt s (fmap (\_ -> ()) v)
如果您不想破坏类型,也应该可以修改Value
的通用表示形式,作为在调用VUTCTime
之前隐藏genericHashWithSalt
的另一种方式。 / p>
data Value = ... -- the original one
instance Hashable Value where
hashWithSalt s (VUTCTime t) = (my custom implementation) s t
hashWithSalt s t = genericHashWithSalt s (genericHideLastConstructor t)
-- something like that...
答案 3 :(得分:2)
您可以创建带有“孔”的类型,然后在hashWithSalt
中填充孔。所以:
{-# LANGUAGE DeriveFunctor, DeriveGeneric, DeriveAnyClass #-}
import Data.Hashable
import Data.Text (Text)
import Data.Time
import GHC.Generics
import qualified Data.ByteString as BS
data ValueF a
= VNull
| VDouble !Double
| VInt !Int
| VText !Text
| VTexts ![Text]
| VByteString !BS.ByteString
| VUTCTime !a
deriving (Hashable, Functor, Generic)
newtype Value = Value (ValueF UTCTime)
instance Hashable Value where
hashWithSalt s (Value (VUTCTime t)) = {- whatever you're going to do here -}
hashWithSalt s (Value v) = hashWithSalt s (() <$ v)
-- OR
-- hashWithSalt s (Value v) = hashWithSalt s (unsafeCoerce v :: Value ())