-
I have my own data type to represent nodes and edges of a graph as follows:
data Node a = Node a deriving (Show, Eq) data Label a = Label a deriving (Show) data Cost = CostI Int | CostF Float deriving (Show) data Edge label node = Edge (Label label, (Node node,Node node), Cost) deriving (Show) -
Now, I create a function to check whether an edge contains 2 nodes or not as follows:
isEdge:: (Eq n) => (Edge l n) -> (Node n, Node n) -> Bool isEdge (Edge (_, (n1,n2), _)) (n3, n4) = result where result = (n1 == n3) && (n2 == n4) -
The function works well, the problem here is if I remove (Eq n) from the function, it fails. So, why is that, even though in the declaration above I declared
Nodeas deriving from Eq class?data Node a = Node a deriving (Show, Eq)
I have my own data type to represent nodes and edges of a graph
Share
The
Eqinstance GHC derives forNode ais something like this:You can view the generated code by compiling with
-ddump-deriv. TheEq aconstraint is needed for obvious reasons. So, GHC couldn’t infer an instance ofEqfor, say,Node (a -> b)since functions can’t be compared.However, the fact that GHC can’t infer an instance of
EqforNode afor someadoesn’t mean it will stop you from constructing a values of typeNode awhereaisn’t an equality type.If you wanted to stop people from constructing non-comparable
Nodes, you could try putting a constraint like this:But now GHC tells us we need a compiler pragma:
OK, let’s add it to the top of our file:
Now compile:
The problem is that now every function using
Nodes will need anEqclass constraint, which is annoying (your functions still need the constraint!). (Also, if your user wants to createNodes using a non-equality type but never tests them for equality, what’s the problem?)There’s actually a way to get GHC to do what you want, however: Generalized Algebraic Data Types (GADTs):
This looks just like your original definition, except that it emphasizes the
Nodevalue constructor (the one formerly on the right hand side of the data declaration) is just a function, which you can add constraints to. Now GHC knows that only equality types can be put intoNodes, and unlike our earlier attempted solution, we can make new functions that don’t need a constraint:We can still derive
EqandShowinstances, but with a slightly different syntax:(Hence the StandaloneDeriving pragma above.)
For this to work, GHC also requires us to add a
Showconstraint to our GADT (if you look at the generated code again, you’ll see the constraints are now gone):And now we can take the
Eqconstraint offisEdge, since GHC can infer it!(This is definitely overkill for such a simple situation — again, if people want to construct nodes with functions inside them, why shouldn’t they? However, GADTs are extremely useful in pretty similar situations when you want to enforce certain properties of your data types. See a cool example).
EDIT (from the future): you can also write
but you still need to enable GADT extensions and derive instances separately. See this thread.