Merge: Introduce Cloneable interface
authorJean Privat <jean@pryen.org>
Tue, 3 Mar 2015 15:04:56 +0000 (22:04 +0700)
committerJean Privat <jean@pryen.org>
Tue, 3 Mar 2015 15:04:56 +0000 (22:04 +0700)
As exposed by #917 a standard API to clone objects could be useful.

This PR propose a Cloneable interface that class can implement.
The specif semantic of what a clone is is domain-dependent, the only suggestion I define is to be POLA and to consider the post condition `self == clone`. It seems sane since the precise semantic of `==` is also domain-dependent. The post-condition is not mandatory since not all classes redefines `==` and maybe some classes could be cloneable without being `==`-able. The reason is that `clone` is instantaneous but the semantic `==` should remain valid all the time. For instance, It could make sense to clone some Iterators but I am not sure what is a sane definition of `==` on them.

As a POC, clone is implemented is the three Array-related classes, Array, ArraySet and ArrayMap.

Pull-Request: #1176
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

lib/standard/collection/abstract_collection.nit
lib/standard/collection/array.nit
lib/standard/kernel.nit
tests/sav/nitg-e/fixme/base_gen_reassign_alt4.res
tests/sav/nitg-e/fixme/base_gen_reassign_alt5.res
tests/sav/nitg-e/fixme/base_gen_reassign_alt6.res
tests/sav/nituml_args3.res
tests/sav/nituml_args4.res
tests/sav/test_new_native_alt1.res

index 858d569..20d1584 100644 (file)
@@ -466,6 +466,30 @@ interface MapRead[K, V]
        # Note: the value is returned *as is*, implementations may want to store the value in the map before returning it
        # @toimplement
        protected fun provide_default_value(key: K): V do abort
+
+       # Does `self` and `other` have the same keys associated with the same values?
+       #
+       # ~~~
+       # var a = new HashMap[String, Int]
+       # var b = new ArrayMap[Object, Numeric]
+       # assert a == b
+       # a["one"] = 1
+       # assert a != b
+       # b["one"] = 1
+       # assert a == b
+       # b["one"] = 2
+       # assert a != b
+       # ~~~
+       redef fun ==(other)
+       do
+               if not other isa MapRead[nullable Object, nullable Object] then return false
+               if other.length != self.length then return false
+               for k, v in self do
+                       if not other.has_key(k) then return false
+                       if other[k] != v then return false
+               end
+               return true
+       end
 end
 
 # Maps are associative collections: `key` -> `item`.
index ae351cf..1fb8581 100644 (file)
@@ -252,6 +252,7 @@ end
 #     assert a == b
 class Array[E]
        super AbstractArray[E]
+       super Cloneable
 
        redef fun [](index)
        do
@@ -393,6 +394,29 @@ class Array[E]
                return true
        end
 
+       # Shallow clone of `self`
+       #
+       # ~~~
+       # var a = [1,2,3]
+       # var b = a.clone
+       # assert a == b
+       # a.add 4
+       # assert a != b
+       # b.add 4
+       # assert a == b
+       # ~~~
+       #
+       # Note that the clone is shallow and elements are shared between `self` and the result.
+       #
+       # ~~~
+       # var aa = [a]
+       # var bb = aa.clone
+       # assert aa == bb
+       # aa.first.add 5
+       # assert aa == bb
+       # ~~~
+       redef fun clone do return to_a
+
        # Concatenation of arrays.
        #
        # Returns a new array built by concatenating `self` and `other` together.
@@ -473,6 +497,7 @@ end
 # A set implemented with an Array.
 class ArraySet[E]
        super Set[E]
+       super Cloneable
 
        # The stored elements.
        private var array: Array[E] is noinit
@@ -519,6 +544,37 @@ class ArraySet[E]
        init with_capacity(i: Int) do _array = new Array[E].with_capacity(i)
 
        redef fun new_set do return new ArraySet[E]
+
+       # Shallow clone of `self`
+       #
+       # ~~~
+       # var a = new ArraySet[Int]
+       # a.add 1
+       # a.add 2
+       # var b = a.clone
+       # assert a == b
+       # a.add 3
+       # assert a != b
+       # b.add 3
+       # assert a == b
+       # ~~~
+       #
+       # Note that the clone is shallow and keys and values are shared between `self` and the result.
+       #
+       # ~~~
+       # var aa = new ArraySet[Array[Int]]
+       # aa.add([1,2])
+       # var bb = aa.clone
+       # assert aa == bb
+       # aa.first.add 5
+       # assert aa == bb
+       # ~~~
+       redef fun clone
+       do
+               var res = new ArraySet[E]
+               res.add_all self
+               return res
+       end
 end
 
 # Iterators on sets implemented with arrays.
@@ -538,6 +594,7 @@ end
 # Associative arrays implemented with an array of (key, value) pairs.
 class ArrayMap[K, E]
        super CoupleMap[K, E]
+       super Cloneable
 
        # O(n)
        redef fun [](key)
@@ -616,6 +673,35 @@ class ArrayMap[K, E]
                end
                return -1
        end
+
+       # Shallow clone of `self`
+       #
+       # ~~~
+       # var a = new ArrayMap[String,Int]
+       # a["one"] = 1
+       # a["two"] = 2
+       # var b = a.clone
+       # assert a == b
+       # a["zero"] = 0
+       # assert a != b
+       # ~~~
+       #
+       # Note that the clone is shallow and keys and values are shared between `self` and the result.
+       #
+       # ~~~
+       # var aa = new ArrayMap[String, Array[Int]]
+       # aa["two"] = [1,2]
+       # var bb = aa.clone
+       # assert aa == bb
+       # aa["two"].add 5
+       # assert aa == bb
+       # ~~~
+       redef fun clone
+       do
+               var res = new ArrayMap[K,E]
+               res.recover_with self
+               return res
+       end
 end
 
 private class ArrayMapKeys[K, E]
index 5cc0547..21b0fc1 100644 (file)
@@ -226,6 +226,25 @@ interface Discrete
        end
 end
 
+# Something that can be cloned
+#
+# This interface introduces the `clone` method used to duplicate an instance
+# Its specific semantic is let to the subclasses.
+interface Cloneable
+       # Duplicate `self`
+       #
+       # The specific semantic of this method is let to the subclasses;
+       # Especially, if (and how) attributes are cloned (depth vs. shallow).
+       #
+       # As a rule of thumb, the principle of least astonishment should
+       # be used to guide the semantic.
+       #
+       # Note that as the returned clone depends on the semantic,
+       # the `==` method, if redefined, should ensure the equality
+       # between an object and its clone.
+       fun clone: SELF is abstract
+end
+
 # A numeric value supporting mathematical operations
 interface Numeric
        super Comparable
index 0856809..2ddb582 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:413)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:432)
 11
 21
 31
index 0856809..2ddb582 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:413)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:432)
 11
 21
 31
index 0856809..2ddb582 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:413)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:432)
 11
 21
 31
index e248df6..24adbed 100644 (file)
@@ -30,6 +30,11 @@ Discrete [
 ]
 Comparable -> Discrete [dir=back arrowtail=open style=dashed];
 
+Cloneable [
+ label = "{interface\nCloneable||+ clone(): SELF\l}"
+]
+Object -> Cloneable [dir=back arrowtail=open style=dashed];
+
 Numeric [
  label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
 ]
index 9a7b30f..81077d2 100644 (file)
@@ -30,6 +30,11 @@ Discrete [
 ]
 Comparable -> Discrete [dir=back arrowtail=open style=dashed];
 
+Cloneable [
+ label = "{interface\nCloneable||+ clone(): SELF\l}"
+]
+Object -> Cloneable [dir=back arrowtail=open style=dashed];
+
 Numeric [
  label = "{interface\nNumeric||+ +(i: OTHER): OTHER\l+ -(i: OTHER): OTHER\l+ unary -(): OTHER\l+ *(i: OTHER): OTHER\l+ /(i: OTHER): OTHER\l+ to_i(): Int\l+ to_f(): Float\l+ is_zero(): Bool\l+ zero(): OTHER\l+ value_of(val: Numeric): OTHER\l}"
 ]
index d21eb0d..cf299e7 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:808)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:894)
 NativeString
 N
 Nit