In my project I have created a data type, that can hold one of a few types of values:
data PhpValue = VoidValue | IntValue Integer | BoolValue Bool
What I wanted to do now, is to have a simple way of checking if two values of the PhpValue
type are of the same constructor (correct me if I'm confused with the terminology here, but basically what I want to check if both are, for example, are IntValue
, without caring about the particular value).
Here is a function I wrote for that:
sameConstructor :: PhpValue -> PhpValue -> Bool sameConstructor VoidValue VoidValue = True sameConstructor (IntValue _) (IntValue _) = True sameConstructor (BoolValue _) (BoolValue _) = True sameConstructor _ _ = False
This works as it should, but I don't really like it: if I add more constructors (like FloatValue Float
) I am going to have to rewrite the function, and it will get bigger as my data definition gets bigger.
The Question: Is there a way of writing such a function, so that its implementation doesn't change when I add more constructors?
For the record: I don't want to change the data
definition, I have enough Monads in the rest of my code as it is ;)
Most notably, in Haskell, functions are not in the Eq typeclass (in general). Not any two functions can be compared for equality in Haskell.
Type constructorOf any specific type a , be it Integer , Maybe String , or even Tree b , in which case it will be a tree of tree of b . The data type is polymorphic (and a is a type variable that is to be substituted by a specific type). So when used, the values will have types like Tree Int or Tree (Tree Boolean) .
() is very often used as the result of something that has no interesting result. For example, an IO action that is supposed to perform some I/O and terminate without producing a result will typically have type IO () .
A data constructor is a "function" that takes 0 or more values and gives you back a new value. A type constructor is a "function" that takes 0 or more types and gives you back a new type.
Take a look at Data.Data
and its toConstr
function. This returns a representation of the constructor which can be compared for equality.
With an extension (you can put {-# LANGUAGE DeriveDataTypeable #-}
at the top of your module), you can have a Data
instance derived for you automatically:
data PhpValue = VoidValue | IntValue Integer | BoolValue Bool deriving (Typeable, Data)
You should then be able to use the toConstr
function to compare by constructor.
Now the following will be true:
toConstr (BoolValue True) == toConstr (BoolValue False)
Using on
from Data.Function
you can now rewrite sameConstructor
to:
sameConstructor = (==) `on` toConstr
This is the same as
sameConstructor l r = toConstr l == toConstr r
I think the version using on
is easier to read at a glance.
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