6

I have a complex and recursive data structure which I have simplified to the following:

data Node = Node { value :: Integer, next :: Node } deriving (Show,Eq)

Given the following expressions:

--Create a circular structure
a = Node 1 b
b = Node 0 a --Tie the knot
c = Node 1 b --Another structure which points to b

The expressions a and c are conceptually equal: they both represent a node which holds the value 1 and points to the expression b. My question is: how do I check that they are indeed equal in a Haskell expression? If I evaluate a == c it will keep evaluating sub-elements in the circular structure forever.

Is it possible to perform such a comparison in Haskell?

EDIT: In my case, I am trying to compare the two for inspection/debugging purposes. But another reason to do this could be for unit testing.

pmlt
  • 113
  • 6
  • Why do you want to do this? – Pubby Jan 21 '13 at 23:22
  • possible duplicate of [Python-"is"-like equality operator for Haskell/GHC](http://stackoverflow.com/questions/5701893/python-is-like-equality-operator-for-haskell-ghc) – Daniel Wagner Jan 21 '13 at 23:36
  • Seeing as I'm doing this project as an exercise to learn Haskell and test all of its possibilities, I would like to avoid GHC-specific extensions if at all possible. – pmlt Jan 21 '13 at 23:40
  • It's not possible without GHC extensions (and it's iffy even with them) unless you give each Node an explicit ID-value and compare them using IDs. – shang Jan 22 '13 at 04:58
  • It's not possible in general. The problem is that semantic equality is not decidable. – augustss Jan 22 '13 at 11:26

2 Answers2

12

First of all, a and b are not equal, but a and c are equal, not just conceptually, but they are in fact the same thing.

To answer your question: there is no drop-in solution to your problem. If you need identity comparison, you first have to establish a notion of identity. One way to do this is to have a Map from keys to nodes:

data Node k =
    Node {
      nodeValue :: Integer,
      nodeNext  :: k
    }

The idea is that you have a separate Map from keys of type k to nodes. However, you can't write an Eq instance for that one. A somewhat elegant way to solve this is to use reflection:

{-# LANGUAGE ScopedTypeVariables #-}

import Data.Reflection

data Node n k =
    Node {
      nodeValue :: Integer,
      nodeNext  :: k
    }

instance (n `Reifies` Map k (Node n k)) => Eq (Node n k) where
    (==) = {- ... -}
        where
        nodeMap :: Map k (Node n k)
        nodeMap = reflect (Proxy :: Proxy n)

Another option that gained some attention recently is the notion of observable sharing.

ertes
  • 4,430
  • 1
  • 18
  • 23
  • Sorry for the mistake, I did mean to say that `a` and `c` were equal, not `a` and `b`. I have corrected my question. Thank you for the clear answer, I will test if any of these solutions work in my case and get back to you as soon as possible! – pmlt Jan 22 '13 at 00:41
  • The observable sharing notion is very interesting but seems to steer away from the philosophy of Haskell. The reason why I marked this as the answer is the very first sentence: "`a` and `c` are equal, not just conceptually, but they are in fact the same thing". That means that if I want to distinguish between two Nodes which hold the same value and point to the same "next" Node, then I need to revise my definition of Node to include some sort of unique ID field. Thank you SO much! – pmlt Jan 22 '13 at 22:49
-1

If you promise to use it only for testing and debugging, and not for application code, you can also use my ghc-heap-view library, which gives you a low-level view onto the Haskell heap and would enable you to implement a comparison:

Prelude> :script /home/jojo/.cabal/share/ghc-heap-view-0.4.0.0/ghci 
Prelude> data Node = Node { value :: Integer, next :: Node } deriving (Show,Eq)
Prelude> let { a = Node 1 b; b = Node 0 a ; c = Node 1 b}
Prelude> take 100 (show a) -- make sure it is evaluated, we are not interested in thunks here
"Node {value = 1, next = Node {value = 0, next = Node {value = 1, next = Node {value = 0, next = Node"
Prelude> take 100 (show c) -- dito
"Node {value = 1, next = Node {value = 0, next = Node {value = 1, next = Node {value = 0, next = Node"
Prelude> System.Mem.performGC
Prelude> :printHeap a
let x0 = Node (S# 1) (Node (S# 0) x0)
in x0
Prelude> :printHeap c
let x2 = Node (S# 0) (Node (S# 1) x2)
in Node (S# 1) x2

But this is certainly not the right thing to do for you now, given you are „doing this project as an exercise to learn Haskell”.

Joachim Breitner
  • 25,395
  • 6
  • 78
  • 139