Reputation: 12103
I'd like to be able to write a function which checks that two values have been built using the same head constructor. This function:
shouldn't be linear in the size of the datatype declaration
should keep working if the datatype is extended
e.g. this is not satisfactory (it is linear and the catchall will invalidate the function if I add any extra constructor):
data E = A Int | B String | C
sameCons :: E -> E -> Bool
sameCons t u = case (t, u) of
(A{}, A{}) -> True
(B{}, B{}) -> True
(C{}, C{}) -> True
_ -> False
In OCaml it is possible to use unsafe functions from the Obj
module to do exactly that. Can we do something similar in Haskell (a ghc-specific solution works too)?
Upvotes: 5
Views: 365
Reputation: 12103
We ended up needing a datatype of constructor names anyway so here is our current solution which does not rely on Data
or GHC.Generics
:
data E = A Int | B String | C
data EName = A_ | B_ | C_ deriving (Eq)
eName :: E -> EName
eName e = case e of
A{} -> A_
B{} -> B_
C{} -> C_
sameCons :: E -> E -> Bool
sameCons = (==) `on` eName
Upvotes: 0
Reputation: 33439
You can also do this with GHC.Generics
, but it's more boilerplate than The Orgazoid's answer.
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeOperators #-}
import Data.Function (on)
import GHC.Generics
class GSameCons f where
gSameCons :: f p -> f p -> Bool
instance GSameCons f => GSameCons (D1 c f) where
gSameCons (M1 a) (M1 b) = gSameCons a b
instance (GSameCons f, GSameCons g) => GSameCons (f :+: g) where
gSameCons (L1 a) (L1 b) = gSameCons a b
gSameCons (R1 a) (R1 b) = gSameCons a b
gSameCons _ _ = False
instance GSameCons (C1 c f) where
gSameCons _ _ = True
data E = A Int | B String | C deriving Generic
sameCons :: (GSameCons (Rep a), Generic a) => a -> a -> Bool
sameCons = gSameCons `on` from
main = do
print (sameCons (A 1) (A 2))
print (sameCons (B "") C)
Upvotes: 3
Reputation: 44634
If you're willing to derive Data
then you're good to go.
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
data E = A Int | B String | C deriving (Typeable, Data)
sameCons :: E -> E -> Bool
sameCons x y = toConstr x == toConstr y
ghci> sameCons (A 1) (A 3)
True
ghci> sameCons (A 1) (C)
False
Upvotes: 6