Let\'s consider a data type with many constructors:
data T = Alpha Int | Beta Int | Gamma Int Int | Delta Int
I want to write a function to che
Look at the Data.Data module, the toConstr
function in particular. Along with {-# LANGUAGE DeriveDataTypeable #-}
that will get you a 1-line solution which works for any type which is an instance of Data.Data
. You don't need to figure out all of SYB!
If, for some reason (stuck with Hugs?), that is not an option, then here is a very ugly and very slow hack. It works only if your datatype is Show
able (e.g. by using deriving (Show)
- which means no function types inside, for example).
constrT :: T -> String
constrT = head . words . show
sameK x y = constrT x == constrT y
constrT
gets the string representation of the outermost constructor of a T
value by showing it, chopping it up into words and then getting the first. I give an explicit type signature so you're not tempted to use it on other types (and to evade the monomorphism restriction).
Some notable disadvantages:
data T2 = Eta Int | T2 :^: T2
)show
, such as many library types.That said, it is Haskell 98... but that's about the only nice thing I can say about it!
Another possible way:
sameK x y = f x == f y
where f (Alpha _) = 0
f (Beta _) = 1
f (Gamma _ _) = 2
-- runtime error when Delta value encountered
A runtime error is not ideal, but better than silently giving the wrong answer.
You can definitely use the generics to eliminate the boilerplate. Your code is a textbook example why I (and many others never use the _
wildcard at top level). While it is tedious to write out all the cases, it is less tedious than dealing with the bugs.
In this happy example I would not only use Dave Hinton's solution but would slap an INLINE pragma on the auxiliary function f
.
You'll need to use a generics library like Scrap Your Boilerplate or uniplate to do this in general.
If you don't want to be so heavy-handed, you can use Dave Hinton's solution, together with the empty record shortcut:
...
where f (Alpha {}) = 0
f (Beta {}) = 1
f (Gamma {}) = 2
So you don't have to know how many args each constructor has. But it obviously still leaves something to be desired.
In some cases, "Scrap Your Boilerplate" library will help.
http://www.haskell.org/haskellwiki/Scrap_your_boilerplate