I assume that your interpreter will work within the State monad. Probably the state will consist of a collection of live objects and whatnot. What you can do, is keep track of a list of available (non-used) ids (represented as Ints) and annotate every Object with an Int, namely its id. This id was taken from the list of available ids and assigned to it at creation time. Thus it cannot be that two Object instances have the same id.
Observe that I talked about Ints. You could go for Integers, but that may eventually become less efficient. Going with Ints does mean that you'll have to add the id of destroyed objects back to the pool (list) of available ids (for otherwise you'd run out, eventually). Thus I envision something like this:
data Object a = Obj Int a
instance Eq (Object a) where
Obj i _ == Obj j _ = i == j
type InterpreterState a = State [Int] a
createObject :: a -> InterpreterState (Object a)
createObject a = do
(i:is) <- get
put is
return $ Obj i a
destroyObject :: Object a -> InterpreterState ()
destroyObject (Obj i a) = do
modify (i:)
(Note that InterpreterState will be much more complex in your case, and createObject and destroyObject should have the object added to/removed from the state. But that's beside the point here.)