他の回答で合理的な方法を使用したくない場合は、完全にサポートされていない方法を使用できます。これは、高速であることが保証されていますが、実際には正しい結果が得られるとは限らず、クラッシュしないことも保証されています。これは、関数を比較しようとしても喜ばしいことに注意してください。これにより、まったく偽の結果が得られます。
{-# 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
もう1つの問題は(おそらく)、これがニュータイプを介してピアリングすることです。だからあなたが持っているなら
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
;として報告します。それ以外の場合は、実際のタグを使用します。