Given two terms t1 t2 of some data type, is there some way to check if t1 and t2 start with the same constructor, without doing some exhaustive case or pattern matching over constructors? Like if my type is Either a b, then I want
checkConst (Left x) (Left y) = True
checkConst (Right x) (Left y) = False
...
and so on without actually doing that pattern matching, and in a way that is generalizable to other types with on the order of 10 constructors. Is there a nice way to do this?
You are probably looking for the generics functionality available in the Data.Data
module. If you define a data type with a derived Data
instance, like so:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
data Foo = Bar1 Int
| Bar2 String String
| Bar3 Double
deriving (Data)
then you can retrieve the constructor for a value with toConstr
:
> toConstr (Bar1 1)
Bar1
> toConstr (Bar2 "hello" "there")
Bar2
>
These are values of type Constr
that can be compared for equality, so you can define:
checkConst :: (Data g) => g -> g -> Bool
checkConst x y = toConstr x == toConstr y
and get:
> checkConst (Bar1 10) (Bar1 20)
True
> checkConst (Bar1 10) (Bar3 20)
False
>
You could use some kind of intermediate type which can uniquely distinguish each constructor and has an Eq
instance. For example, you could use an Int
to distinguish each data constructor, and then compare the values.
checkConst x y = toInt x == toInt y
where
toInt (Left _) = 1 :: Int
toInt (Right _) = 2
If your type implements Data
from Data.Data
, you could use toConstr
to use Constr
as your intermediate type. In the case of Either
this isn't necessary because you could use isRight x == isRight y
, but for a data type with 10 constructors, this would be cleaner.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With