Search code examples
eiffel

twin vs deep_twin, is_equal vs is_deep_equal


What is the difference between twin and deep_twin which implies (and ensures) the difference between is_equal and is_deep_equal in eiffel?


Solution

  • twin makes a copy of the current object. If the object references other objects, by default it does not make copies of these other objects. That's why it is said that the function makes a "shallow" copy. On the contrary, deep_twin makes a copy not only of the current object, but also (recursively) copies of all other objects that are referenced by the current one. Here is an example

    class A create make feature
       make (value: STRING) do item := value end
       item: STRING
    end
    
    a: A
    ...
    a.item = a.twin.item -- Evaluates to True
    a.item = a.deep_twin.item -- Evaluates to False
    

    In the first comparison, the value of item in the twin is the same string object as in the original object. In the second comparison, the value of item in the twin is a new string object, equal to a.item. In other words, if a.item = s, then a.twin.item = s, but a.twin.item = s1 where s1 /= s, but s1 ~ s.

    is_equal and is_deep_equal are conterparts for the functions above. The first makes a "shallow" equality test, the second — a deep one. In the example, we have

    a.is_equal (a.twin) -- Evaluates to True
    a.is_deep_equal (a.twin) -- Evaluates to True
    a.is_equal (a.deep_twin) -- Evaluates to False
    a.is_deep_equal (a.deep_twin) -- Evaluates to True
    

    If objects are "shallow" equal, they are also "deep" equal. But the reverse is not true.

    All the above is valid with the assumption that the default versions of is_equal and twin are used. The features can be redefined to go deeper. For the class above we can add

       is_equal (other: like Current): BOOLEAN
          do
                -- The default version is "Result := item = other.item"
             Result := item ~ other.item -- Compare content rather than references
          end
    
       copy (other: like Current) -- twin is defined in terms of this procedure
          do
                -- The default version is "item := other.item"
             item := other.item.twin -- Use a copy instead of the reference
          end
    

    With this redefinition, twin/deep_twin and is_equal/is_deep_equal become almost the same.