如果您不想在其他答案中使用任何合理的方式,则可以使用完全不受支持的方式,这种方式可以保证快速,但实际上不能保证给出正确的结果,甚至不会崩溃。请注意,这甚至会很乐意尝试比较函数,因为它会给出完全虚假的结果。
{-# language MagicHash, BangPatterns #-}
module DangerZone where
import GHC.Exts (Int (..), dataToTag#)
import Data.Function (on)
{-# INLINE getTag #-}
getTag :: a -> Int
getTag !a = I# (dataToTag a)
sameConstr :: a -> a -> Bool
sameConstr = (==) `on` getTag
另一个问题(可以说)是它通过新类型对等。所以如果你有
newtype Foo a = Foo (Maybe a)
然后
sameConstr (Foo (Just 3)) (Foo Nothing) == False
即使它们是使用Foo
构造函数构建的。您可以通过使用 中的一些机制来解决此问题GHC.Generics
,但没有与使用未优化的泛型相关的运行时成本。这变得很毛茸茸!
{-# language MagicHash, BangPatterns, TypeFamilies, DataKinds,
ScopedTypeVariables, DefaultSignatures #-}
import Data.Proxy (Proxy (..))
import GHC.Generics
import Data.Function (on)
import GHC.Exts (Int (..), dataToTag#)
--Define getTag as above
class EqC a where
eqConstr :: a -> a -> Bool
default eqConstr :: forall i q r s nt f.
( Generic a
, Rep a ~ M1 i ('MetaData q r s nt) f
, GNT nt)
=> a -> a -> Bool
eqConstr = genEqConstr
-- This is separated out to work around a bug in GHC 8.0
genEqConstr :: forall a i q r s nt f.
( Generic a
, Rep a ~ M1 i ('MetaData q r s nt) f
, GNT nt)
=> a -> a -> Bool
genEqConstr = (==) `on` modGetTag (Proxy :: Proxy nt)
class GNT (x :: Bool) where
modGetTag :: proxy x -> a -> Int
instance GNT 'True where
modGetTag _ _ = 0
instance GNT 'False where
modGetTag _ a = getTag a
这里的关键思想是我们查看与类型的通用表示相关联的类型级元数据,以确定它是否是新类型。如果是,我们将其“标签”报告为0
; 否则我们使用它的实际标签。