diff --git a/CHANGELOG.md b/CHANGELOG.md index a70a001f5dc1..831cbed7d27a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -184,6 +184,7 @@ - [Short-hand syntax for `order_by` added.][3643] - [Expanded `Table.at` to support index access and added `Table.column_count` method.][3644] +- [Removed `Array.set_at`.][3634] [debug-shortcuts]: https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug @@ -290,6 +291,7 @@ [3644]: https://github.com/enso-org/enso/pull/3644 [3648]: https://github.com/enso-org/enso/pull/3648 [5250]: https://github.com/enso-org/enso/pull/5250 +[3634]: https://github.com/enso-org/enso/pull/3634 #### Enso Compiler @@ -370,7 +372,7 @@ [3631]: https://github.com/enso-org/enso/pull/3631 [3633]: https://github.com/enso-org/enso/pull/3633 [3637]: https://github.com/enso-org/enso/pull/3637 -[3633]: https://github.com/enso-org/enso/pull/3641 +[3641]: https://github.com/enso-org/enso/pull/3641 [3658]: https://github.com/enso-org/enso/pull/3658 # Enso 2.0.0-alpha.18 (2021-10-12) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso index 21b349b56984..f284f50fe124 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso @@ -23,22 +23,6 @@ type Array at : Integer -> Any at self index = @Builtin_Method "Array.at" - ## Set the cell at the specified index to the provided value, returning - the array. - - Arguments: - - index: The position in the array to set. - - value: The value to set at position index. - - The array is mutated in place, and only returned to facilitate a natural - programming style in Enso. - - ? Safety - If index < 0 or index >= self.length, then this operation will result - in an Invalid_Array_Index_Error exception. - set_at : Integer -> Any -> Array - set_at self index value = @Builtin_Method "Array.set_at" - ## Gets the length of the array this. > Example diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso index 1bf88cb4f40e..22b4060b1b33 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso @@ -5,6 +5,10 @@ from Standard.Base.Data.Index_Sub_Range import While, By_Index, Sample, Every import Standard.Base.Polyglot.Proxy_Polyglot_Array import Standard.Base.Random +polyglot java import java.util.ArrayList +polyglot java import java.lang.IndexOutOfBoundsException +polyglot java import org.enso.base.Array_Utils + ## Creates a new vector of the given length, initializing elements using the provided constructor function. @@ -26,9 +30,9 @@ import Standard.Base.Random Vector.new my_vec.length (ix -> my_vec.at ix) new : Number -> (Number -> Any) -> Vector Any new length constructor = - arr = Array.new length - 0.up_to length . each ix-> arr.set_at ix (constructor ix) - Vector arr + builder = (0.up_to length).fold (new_builder length) builder-> ix-> + builder.append (constructor ix) + builder.to_vector ## Creates a new vector of the given length, filling the elements with the provided constant. @@ -46,9 +50,9 @@ new length constructor = Vector.fill length=50 item=42 fill : Number -> Any -> Vector Any fill length ~item = - arr = Array.new length - 0.up_to length . each ix-> arr.set_at ix item - Vector arr + builder = (0.up_to length).fold (new_builder length) builder-> _-> + builder.append item + builder.to_vector ## Creates a new vector builder instance. @@ -63,18 +67,18 @@ fill length ~item = - capacity: Initial capacity of the Vector.Builder > Example - Construct a vector using a builder that contains the items 1 to 10. + Construct a vector using a builder that contains the items 1 to 5. example_new_builder = - builder = Vector.new_builder 10 + builder = Vector.new_builder 5 do_build start stop = builder.append start if start >= stop then Nothing else @Tail_Call do_build start+1 stop - do_build 1 10 + do_build 1 5 builder.to_vector new_builder : Integer -> Builder -new_builder (capacity=1) = Builder.new capacity +new_builder (capacity=10) = Builder.new capacity ## ADVANCED @@ -113,12 +117,12 @@ type Vector Vector.fill length=50 item=42 type Vector storage + ## PRIVATE to_array self = arr = self.storage.to_array - case arr of - Array -> - arr - _ -> + case Meta.meta arr of + Meta.Primitive _ -> arr + _ -> len = self.storage.length a = Array.new len Array.copy arr 0 a 0 len @@ -986,8 +990,7 @@ type Builder A builder type for Enso vectors. Arguments: - - to_array: The accumulator for the new vector. - - length: The current length of the vector being built. + - java_builder: The accumulator for the new vector. A vector builder is a mutable data structure, that allows to gather a number of elements and then convert them to a vector. This is @@ -1007,7 +1010,14 @@ type Builder @Tail_Call do_build new_builder start+1 stop builder = do_build Vector.new_builder 1 10 builder.to_vector - type Builder to_array length + + ! TODO + We may want to revisit the fold pattern - it is required for correct + propagation of dataflow errors, but it is very easy to forget about it + and get wrong error propagation. Instead we may want to have a `Ref` + inside of the Builder. Any error detected during `append` could set + that `Ref` and then `to_vector` could propagate that error. + type Builder java_builder ## Creates a new builder. @@ -1019,21 +1029,11 @@ type Builder Vector.new_builder new : Integer->Builder - new (capacity=1) = Builder (Array.new capacity) 0 - - ## Returns the current capacity (i.e. the size of the underlying storage) - of this builder. - - > Example - Get the capacity of a new builder. - - Vector.new_builder.capacity - capacity : Integer - capacity self = self.to_array.length + new (capacity=10) = Builder (ArrayList.new capacity) ## Checks if this builder is empty. is_empty : Boolean - is_empty self = self.length == 0 + is_empty self = self.java_builder.isEmpty ## Checks if this builder is not empty. not_empty : Boolean @@ -1056,6 +1056,27 @@ type Builder self.unsafe_append item self + ## Appends a part of a given vector to this builder + + Arguments: + - vector: The vector from which the elements are sourced. + - start: The start index of the range to append. + - end: The end index (the first index after the last element to be + appended) of the range to be appended. + + > Example + Append a part of the vector. + + builder = Vector.new_builder + builder . append_vector_range [20, 30, 40, 50] 1 3 . to_vector == [30, 40] + append_vector_range : Vector Any ! Error -> Builder ! Error + append_vector_range self vector start end = + subrange = vector.slice start end + ## This workaround is needed because + `self.java_builder.addAll subrange.to_array` fails with + `Unsupported argument types: [Array]`. + Array_Utils.appendToArrayList self.java_builder subrange.to_array + ## PRIVATE Appends a new element into this builder. @@ -1075,17 +1096,7 @@ type Builder Vector.new_builder.unsafe_append 10 unsafe_append : Any -> Nothing - unsafe_append self item = case self.capacity > self.length of - True -> - self.to_array.set_at self.length item - Unsafe.set_atom_field self 1 (self.length + 1) - False -> - old_array = self.to_array - new_array = Array.new old_array.length*2 - Array.copy old_array 0 new_array 0 old_array.length - Unsafe.set_atom_field self 0 new_array - self.append item - Nothing + unsafe_append self item = self.java_builder.add item ## Gets an element from the vector at a specified index (0-based). @@ -1096,7 +1107,7 @@ type Builder at : Integer -> Any ! Index_Out_Of_Bounds_Error at self index = actual_index = if index < 0 then self.length + index else index - Panic.catch Invalid_Array_Index_Error (self.to_array.at actual_index) _-> + Panic.catch IndexOutOfBoundsException (self.java_builder.get actual_index) _-> Error.throw (Index_Out_Of_Bounds_Error index self.length) ## Checks whether a predicate holds for at least one element of this builder. @@ -1107,7 +1118,7 @@ type Builder exists : (Any -> Boolean) -> Boolean exists self predicate = - 0.up_to self.length . exists (idx -> (predicate (self.to_array.at idx))) + 0.up_to self.length . exists (idx -> (predicate (self.java_builder.get idx))) ## Converts this builder to a vector containing all the appended elements. @@ -1122,10 +1133,10 @@ type Builder bldr.to_vector to_vector : Vector Any to_vector self = - old_array = self.to_array - new_array = Array.new self.length - Array.copy old_array 0 new_array 0 self.length - Vector new_array + ## This creates a fresh copy of the builders storage, so any future + changes to the builder will not affect the returned vector. + new_array = self.java_builder.toArray + from_polyglot_array new_array ## UNSTABLE @@ -1211,21 +1222,17 @@ slice_many_ranges vector ranges = new_length = ranges.fold 0 acc-> descriptor-> case descriptor of Integer -> acc+1 Range _ _ _ -> acc+descriptor.length - arr = Array.new new_length - ranges.fold 0 start_ix-> descriptor-> case descriptor of + builder = new_builder new_length + ranges.each descriptor-> case descriptor of Integer -> - arr.set_at start_ix (vector.unsafe_at descriptor) - start_ix+1 + builder.append (vector.unsafe_at descriptor) Range start end step -> case step == 1 of True -> - len = end-start - Array.copy vector.to_array start arr start_ix len - start_ix+len + builder.append_vector_range vector start end False -> - descriptor.each_with_index within_range_ix-> descriptor_ix-> - arr.set_at start_ix+within_range_ix (vector.unsafe_at descriptor_ix) - start_ix+descriptor.length - Vector arr + descriptor.each ix-> + builder.append (vector.unsafe_at ix) + builder.to_vector ## PRIVATE Takes a list of descriptors and returns a new one where ranges with diff --git a/distribution/lib/Standard/Image/0.0.0-dev/src/Data/Matrix/Internal.enso b/distribution/lib/Standard/Image/0.0.0-dev/src/Data/Matrix/Internal.enso index 868b14e63395..c4bff53d691d 100644 --- a/distribution/lib/Standard/Image/0.0.0-dev/src/Data/Matrix/Internal.enso +++ b/distribution/lib/Standard/Image/0.0.0-dev/src/Data/Matrix/Internal.enso @@ -18,8 +18,8 @@ core_op : Mat -> Any -> (Mat -> Scalar -> Mat -> Nothing) -> Nothing core_op mat value function = result = Mat.new scalar = case value of - Vector.Vector arr -> - Scalar.new arr + Vector.Vector _ -> + Scalar.new value.to_array Matrix.Matrix m -> if ((m.rows == mat.rows) && (m.cols == mat.cols) && (m.channels == mat.channels)) then m else Panic.throw Matrix.Dimensions_Not_Equal _ -> diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso index 39b76ca0af06..b338868669e2 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso @@ -346,8 +346,8 @@ Error.should_equal self _ frames_to_skip=0 = fail_match_on_unexpected_error self example_should_equal = 1.00000001 . should_equal 1.00000002 epsilon=0.0001 -Decimal.should_equal : Decimal -> Decimal -> Integer -> Assertion -Decimal.should_equal self that epsilon=0 frames_to_skip=0 = +Number.should_equal : Decimal -> Decimal -> Integer -> Assertion +Number.should_equal self that epsilon=0 frames_to_skip=0 = matches = case that of Number -> self.equals that epsilon _ -> False diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Geo_Map.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Geo_Map.enso index 8551cdcb4b1b..2fb7e612b5ae 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Geo_Map.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Geo_Map.enso @@ -13,11 +13,11 @@ import Standard.Visualization.Helpers json_from_table : Table.Table -> Object json_from_table table = names = ['label', 'latitude', 'longitude', 'radius', 'color'] - pairs = names.filter_map <| name-> + pairs = names.map <| name-> column = table.lookup_ignore_case name - column.when_valid ["df_" + name, column.to_vector] + column.when_valid ["df_" + name, column.to_vector] . catch Nothing - Json.from_pairs pairs + Json.from_pairs <| pairs.filter (x -> x.is_nothing.not) ## PRIVATE diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Helpers.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Helpers.enso index 256f19f3178f..9fd788f873ad 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Helpers.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Helpers.enso @@ -4,16 +4,6 @@ import Standard.Table.Data.Column import Standard.Table.Data.Storage import Standard.Table.Data.Table -## PRIVATE - - Maps the vector using the given function. Filters out all error values. - - Arguments: - - f: unary invokable that is applied to each vector element. Non-error - values are returned in the resulting vector. Error values are dropped. -Vector.Vector.filter_map : Any -> Vector -Vector.Vector.filter_map self f = self.map f . filter .is_valid - ## PRIVATE Returns the given value if this is not an error. Propagates error otherwise. @@ -116,9 +106,8 @@ Table.Table.all_columns self = - text: the case-insensitive name of the searched column. Table.Table.lookup_ignore_case : Text -> Column ! Nothing Table.Table.lookup_ignore_case self name = - ret = self.all_columns.find <| col-> + self.all_columns.find <| col-> col.name.equals_ignore_case name - ret ## PRIVATE diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Scatter_Plot.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Scatter_Plot.enso index 5f063a82243b..2d2fdf71b00b 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Scatter_Plot.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Scatter_Plot.enso @@ -107,8 +107,8 @@ No_Fallback_Column.to_display_text self = Generates JSON that describes points data. Table.Table.point_data : Table -> Object Table.Table.point_data self = - get_point_data field = field.lookup_in self . rename field.name - columns = Point_Data.all_fields.filter_map get_point_data + get_point_data field = field.lookup_in self . rename field.name . catch Any (_->Nothing) + columns = Point_Data.all_fields.map get_point_data . filter (x -> x.is_nothing.not) (0.up_to self.row_count).to_vector.map <| row_n-> pairs = columns.map column-> value = column.at row_n . catch_ Nothing diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java index e65a8fcfb860..530280eba7d4 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java @@ -8,7 +8,6 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; -import org.enso.interpreter.dsl.AcceptsError; import org.enso.interpreter.dsl.Builtin; import org.enso.interpreter.node.expression.builtin.error.InvalidArrayIndexError; import org.enso.interpreter.runtime.Context; @@ -111,13 +110,6 @@ public Object get(long index) { return getItems()[(int) index]; } - @Builtin.Method(name = "setAt", description = "Gets an array element at the given index.") - @Builtin.WrapException(from = IndexOutOfBoundsException.class, to = InvalidArrayIndexError.class) - public Object set(long index, @AcceptsError Object value) { - getItems()[(int) index] = value; - return this; - } - /** * Exposes an index validity check through the polyglot API. * @@ -129,21 +121,6 @@ boolean isArrayElementReadable(long index) { return index < getArraySize() && index >= 0; } - @ExportMessage - void writeArrayElement(long index, Object value) { - items[(int) index] = value; - } - - @ExportMessage - boolean isArrayElementModifiable(long index) { - return isArrayElementReadable(index); - } - - @ExportMessage - boolean isArrayElementInsertable(long index) { - return false; - } - @ExportMessage String toDisplayString(boolean b) { return toString(); diff --git a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java index 4933aa251591..4bf9ec387732 100644 --- a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java +++ b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java @@ -1,5 +1,8 @@ package org.enso.base; +import java.util.ArrayList; +import java.util.List; + public class Array_Utils { /** * This function forces the polyglot conversion of an Enso array into a `byte[]`. This allows for @@ -11,4 +14,9 @@ public class Array_Utils { public static byte[] ensureByteArray(byte[] input) { return input; } + + /** A temporary workaround to be able to efficiently append an array to `ArrayList`. */ + public static void appendToArrayList(ArrayList builder, List list) { + builder.addAll(list); + } } diff --git a/test/Tests/src/Data/Array_Spec.enso b/test/Tests/src/Data/Array_Spec.enso index f262a4f3f225..85443f999ab6 100644 --- a/test/Tests/src/Data/Array_Spec.enso +++ b/test/Tests/src/Data/Array_Spec.enso @@ -2,40 +2,64 @@ from Standard.Base import all import Standard.Test +polyglot java import java.util.ArrayList + Array.method self = 0 -spec = Test.group "Arrays" <| - Test.specify "should be able to be converted to a visualization rep" <| - arr = Vector.fill 1000 0 . to_array - text = arr.to_default_visualization_data - json = Json.parse text - as_vec = json.into (Vector.Vector Number) - as_vec.should_equal <| Vector.fill 100 0 +## Returns an array with the same contents as the given vector, surely backed by + the Enso Array primitive. +make_enso_array vector = + enso_array = Array.new vector.length + Array.copy vector.to_array 0 enso_array 0 vector.length + enso_array + +## Returns an array with the same contents as the given vector, surely backed by + a Java array. +make_java_array vector = + builder = ArrayList.new + vector.each x-> + builder.add x + builder.toArray +test_arrays array_from_vector = Test.specify "should allow accessing elements" <| - arr = [1, 2, 3] . to_array + arr = array_from_vector [1, 2, 3] arr.at 0 . should_equal 1 arr.at 2 . should_equal 3 - Test.specify "should allow setting elements" <| - arr = [1, 2, 3] . to_array - arr.set_at 1 10 - arr.at 1 . should_equal 10 - Vector.from_polyglot_array arr . should_equal [1, 10, 3] - Test.specify "should panic on out of bounds access" <| - arr = [1, 2, 3] . to_array + arr = array_from_vector [1, 2, 3] Test.expect_panic_with (arr.at -1) Invalid_Array_Index_Error Test.expect_panic_with (arr.at 3) Invalid_Array_Index_Error - Test.expect_panic_with (arr.set_at 3 100) Invalid_Array_Index_Error - Test.specify "should allow for functional dispatch on a method defined in this module" - arr = [1, 2, 3] . to_array - arr.method . should_equal 0 +spec = + Test.group "Enso Arrays" <| + test_arrays make_enso_array + + Test.specify "should allow for functional dispatch on a method defined in this module" <| + arr = make_enso_array [1, 2, 3] + arr.method . should_equal 0 + + Test.specify "should propagate dataflow errors" <| + err = Error.throw (Illegal_State_Error "Foo") + res = Array.new err + res . should_fail_with Illegal_State_Error + + Test.specify "should be able to be converted to a visualization rep" <| + arr = make_enso_array (Vector.fill 1000 0) + text = arr.to_default_visualization_data + json = Json.parse text + as_vec = json.into (Vector.Vector Number) + as_vec.should_equal <| Vector.fill 100 0 + + Test.group "Polyglot Arrays" <| + test_arrays make_java_array - Test.specify "should propagate dataflow errors" <| - err = Error.throw (Illegal_State_Error "Foo") - res = Array.new err - res . should_fail_with Illegal_State_Error + Test.specify "should be able to be converted to a visualization rep" pending="`to_default_visualization_data` does not work for polyglot arrays" <| + arr = make_java_array (Vector.fill 1000 0) + text = arr.to_default_visualization_data + json = Json.parse text + as_vec = json.into (Vector.Vector Number) + as_vec.should_equal <| Vector.fill 100 0 main = Test.Suite.run_main spec diff --git a/test/Tests/src/Data/Vector_Spec.enso b/test/Tests/src/Data/Vector_Spec.enso index 3a6e13b7cf4a..78cc95a6e180 100644 --- a/test/Tests/src/Data/Vector_Spec.enso +++ b/test/Tests/src/Data/Vector_Spec.enso @@ -412,10 +412,10 @@ spec = Test.group "Vectors" <| small_expected = [T 4 0, T 1 3, T 1 8, T -1 10, T -1 1, T -20 0] small_vec.sort order=Sort_Direction.Descending . should_equal small_expected - Test.specify "should be able to map over errors" <| - fail a = Error.throw <| My_Error a - [fail 1].map (x -> x.catch Any (x -> x.a)) . should_equal [1] - [1].map fail . map .catch . should_equal [My_Error 1] + Test.specify "should correctly propagate error through map" <| + [1, 2, 3].map Error.throw . catch . should_equal 1 + fun a = if a == 3 then Error.throw (My_Error a) else a + [1, 2, 3, 4].map fun . catch My_Error . should_equal (My_Error 3) Test.specify "should be able to be efficiently converted to a visualisation" <| vec = Vector.fill 1000 0 diff --git a/test/Visualization_Tests/src/Geo_Map_Spec.enso b/test/Visualization_Tests/src/Geo_Map_Spec.enso index eb351b486230..c2e5d74363d2 100644 --- a/test/Visualization_Tests/src/Geo_Map_Spec.enso +++ b/test/Visualization_Tests/src/Geo_Map_Spec.enso @@ -8,9 +8,10 @@ import Standard.Test import project.Helpers -spec = - expect value expected_json_text = +spec = + expect value expected_json_text = result = Geo_Map.process_to_json_text value + IO.println result Json.parse result . should_equal <| Json.parse expected_json_text Test.group "Geo_Map" <|