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>
# 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`.
# assert a == b
class Array[E]
super AbstractArray[E]
+ super Cloneable
redef fun [](index)
do
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.
# A set implemented with an Array.
class ArraySet[E]
super Set[E]
+ super Cloneable
# The stored elements.
private var array: Array[E] is noinit
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.
# 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)
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]
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
-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
-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
-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
]
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}"
]
]
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}"
]
-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