これは明らかに行き過ぎであるが、ここであなたは自動的にあなたのレコード内の同じタイプのすべてのフィールドが同じ値を持っている場合はtrueを返すような関数fieldsMatch :: FieldsMatch a => a -> Bool
を提供型クラスFieldsMatch
を生成することができますGHC.Generics
ベースのソリューションです。
{-# LANGUAGE TypeOperators, ExistentialQuantification, DefaultSignatures,
FlexibleContexts #-}
module FieldsMatch (FieldsMatch(..)) where
import GHC.Generics
import Data.Typeable
-- `Some` is an existential type that we need to store each field
data Some = forall a. (Eq a, Typeable a) => Some a
-- This is the class we will be deriving
class FieldsMatch a where
-- in general, this is the type of `fieldsMatch`...
fieldsMatch :: a -> Bool
-- ... except the default implementation has slightly different constraints.
default fieldsMatch :: (Generic a, GetFields (Rep a)) => a -> Bool
fieldsMatch = noneDiffering . getFields . from
where
noneDiffering :: [Some] -> Bool
noneDiffering [] = True
noneDiffering (x:xs) = all (notDiffering x) xs && noneDiffering xs
notDiffering :: Some -> Some -> Bool
Some x `notDiffering` Some y = case cast y of
Nothing -> True
Just z -> x == z
class GetFields f where
-- | This function takes the generic representation of a datatype and
-- recursively traverses it to collect all its fields. These need to
-- have types satisfying `Eq` and `Typeable`.
getFields :: f a -> [Some]
instance (GetFields a, GetFields b) => GetFields (a :*: b) where
getFields (l :*: r) = getFields l ++ getFields r
instance (GetFields a, GetFields b) => GetFields (a :+: b) where
getFields (L1 l) = getFields l
getFields (R1 r) = getFields r
instance GetFields U1 where
getFields U1 = []
instance (Typeable a, Eq a) => GetFields (K1 i a) where
getFields (K1 x) = [Some x]
instance GetFields a => GetFields (M1 i t a) where
getFields (M1 x) = getFields x
default fieldsMatch :: (Generic a, GetFields (Rep a)) => a -> Bool
fieldsMatch = noneDiffering . getFields . from
where
noneDiffering :: [Some] -> Bool
noneDiffering [] = True
noneDiffering (x:xs) = all (notDiffering x) xs || noneDiffering xs
notDiffering :: Some -> Some -> Bool
Some x `notDiffering` Some y = case cast y of
Nothing -> True
Just z -> x == z
あなたはGHCiの中でこれを試してみることができます。
ghci> :set -XDeriveGeneric
ghci> data Foo b = Foo Int Int b Bool deriving (Generic)
ghci> instance (Eq b, Typeable b) => FieldsMatch (Foo b)
ghci> Foo 1 1 True True -- fields of the same type are equal
True
ghci> Foo 1 2 True (1,2) -- 1 /= 2 even though they are the same type
False
この質問はかなり独特として私を打ちます。なぜそんなことが欲しいの?レコードは、通常、そのような操作に必要なものではありません。おそらく、あなたは長さインデックスのベクトルが欲しいですか? – dfeuer