diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aa3430da598..7b83a8dcd887 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -651,6 +651,7 @@ - [Added `Decimal.round`.][9672] - [Implemented write support for Enso Cloud files.][9686] - [Added `recursive` option to `File.delete`.][9719] +- [Added `Vector.build`.][9725] [debug-shortcuts]: https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug @@ -952,6 +953,7 @@ [9672]: https://github.com/enso-org/enso/pull/9672 [9686]: https://github.com/enso-org/enso/pull/9686 [9719]: https://github.com/enso-org/enso/pull/9719 +[9725]: https://github.com/enso-org/enso/pull/9725 #### Enso Compiler 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 05264d1b6b8c..dbb9f7c757f4 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 @@ -91,11 +91,13 @@ type Vector a Vector.collect (List.Cons 1 <| List.Cons 2 <| List.Nil) .x .xs stop_at=(_==List.Nil) collect : Any -> (Any -> Any) -> (Any -> Any) -> Integer | Nothing -> (Any -> Boolean) -> Vector Any collect seq element:(Any -> Any) next:(Any -> Any) limit:(Integer | Nothing)=Nothing stop_at:(Any -> Boolean)=(_==Nothing) = - b = Vector.new_builder (if limit.is_nothing then 10 else limit) - iterate item remaining = if remaining == 0 || (stop_at item) then b.to_vector else - b.append <| element item - @Tail_Call iterate (next item) (if remaining.is_nothing then Nothing else remaining-1) - iterate seq limit + Vector.build initial_capacity=(if limit.is_nothing then 10 else limit) builder-> + iterate item remaining = + done = remaining == 0 || (stop_at item) + if done.not then + builder.append <| element item + @Tail_Call iterate (next item) (if remaining.is_nothing then Nothing else remaining-1) + iterate seq limit ## PRIVATE ADVANCED @@ -145,14 +147,15 @@ type Vector a ## PRIVATE ADVANCED + DEPRECATED Creates a new vector builder instance. - A vector builder is a mutable data structure, that allows for gathering + A vector `Builder` is a mutable data structure, that allows for gathering a number of elements and then converting them into a vector. This is particularly useful when the number of elements is not known upfront. - A vector allows to store an arbitrary number of elements in linear memory. It - is the recommended data structure for most applications. + A vector allows to store an arbitrary number of elements in linear + memory. It is the recommended data structure for most applications. Arguments: - capacity: Initial capacity of the Vector.Builder @@ -171,6 +174,51 @@ type Vector a new_builder : Integer -> Builder new_builder (capacity=10) = Builder.new capacity + ## PRIVATE + ADVANCED + Creates a new `Vector` by passing a `Builder` to the provided function. + + A vector `Builder` is a mutable data structure, that allows for gathering + a number of elements and then converting them into a vector. This is + particularly useful when the number of elements is not known upfront. + + `.build` creates a new `Builder`, passes it to the provided function, + which can add elements using the `Builder`'s `.append` method. When the + function is done, `.build` then closes the `Builder and returns the + resulting `Vector`. + + The provided function should call `.append` to add new elements to the + `Builder`. The return value of the provided function is not used, unless + it is a dataflow error, in which case the `Vector` is not built, and the + dataflow error is propagted instead. + + A vector allows to store an arbitrary number of elements in linear + memory. It is the recommended data structure for most applications. + + Arguments: + - function: a function taking a `Builder` and using it to add elements. + - initial_capacity: Initial capacity of the Vector.Builder; this is for + pre-allocation and does not affect the size of the resulting `Vector`. + + ! Error Conditions + + - If the provided function throws a dataflow error, the `Vector` is not + built, and the error is propagted instead. + + > Example + Construct a vector using a builder that contains the items 1 to 5. + + Vector.build builder-> + builder.append 1 + builder.append 2 + builder.append 3 + # => [1, 2, 3] + build : (Builder -> Any) -> Integer -> Vector + build (function : Builder -> Any) (initial_capacity : Integer = 10) -> Vector = + builder = Builder.new initial_capacity + result = function builder + result.if_not_error builder.to_vector + ## PRIVATE ADVANCED @@ -1207,9 +1255,16 @@ type Builder Appends a new element into this builder and returns it, propagating any errors that the provided element could have contained. + Returns the builder, unless `item` is a dataflow error, in which case the + error is propagated. + Arguments: - item: The item to append to the vector builder. + ! Error Conditions + + - If `item` is a dataflow error, the error is propagated. + > Example Append two items. diff --git a/test/Base_Tests/src/Data/Vector_Spec.enso b/test/Base_Tests/src/Data/Vector_Spec.enso index 7977c1f8fbd7..a7f56bdb0ef5 100644 --- a/test/Base_Tests/src/Data/Vector_Spec.enso +++ b/test/Base_Tests/src/Data/Vector_Spec.enso @@ -420,27 +420,25 @@ type_spec suite_builder name alter = suite_builder.group name group_builder-> group_builder.specify "should allow applying a function to each element" <| vec = alter [1, 2, 3, 4] - vec_mut = Vector.new_builder - vec.each vec_mut.append - vec_mut.to_vector . should_equal vec + result = Vector.build builder-> + vec.each builder.append + result . should_equal vec group_builder.specify "should accept changed elements" <| - vec_mut = Vector.new_builder - vec_mut.append 1 - vec_mut.append 1.1 - vec_mut.append Nothing + vec = alter <| Vector.build builder-> + builder.append 1 + builder.append 1.1 + builder.append Nothing - vec = alter vec_mut.to_vector vec.length . should_equal 3 vec.at 0 . should_equal 1 vec.at 1 . should_equal 1.1 vec.at 2 . should_equal Nothing group_builder.specify "should accept Nothing" <| - vec_mut = Vector.new_builder - vec_mut.append Nothing + vec = alter <| Vector.build builder-> + builder.append Nothing - vec = alter vec_mut.to_vector vec.length . should_equal 1 vec.at 0 . should_equal Nothing @@ -778,21 +776,19 @@ type_spec suite_builder name alter = suite_builder.group name group_builder-> group_builder.specify "should correctly propagate state through each" <| v = State.run Number 77 <| - b = Vector.new_builder - (alter ["A", "B"]).each x-> - b.append x - b.append (State.get Number).to_text - b.to_vector + Vector.build builder-> + (alter ["A", "B"]).each x-> + builder.append x + builder.append (State.get Number).to_text v.should_equal ['A', '77', 'B', '77'] group_builder.specify "should correctly propagate state through map" <| v = State.run Number 55 <| - b = Vector.new_builder - (alter ["X", "Y"]).map x-> - b.append x - b.append (State.get Number).to_text - b.to_vector + Vector.build builder-> + (alter ["X", "Y"]).map x-> + builder.append x + builder.append (State.get Number).to_text v.should_equal ['X', '55', 'Y', '55'] @@ -1034,6 +1030,30 @@ add_specs suite_builder = v = Vector.collect l .x .xs limit=30 stop_at=(_==List.Nil) v . should_equal [1, 2, 3] + suite_builder.group "Vector.build" group_builder-> + group_builder.specify "Should be able to build a vector" <| + v = Vector.build builder-> + builder.append 1 + builder.append 2 + builder.append Nothing + builder.append 3 + v . should_equal [1, 2, Nothing, 3] + + v2 = Vector.build builder-> + _ = builder # Do nothing + v2 . should_equal [] + + v3 = Vector.build initial_capacity=20 builder-> + builder.append 1 + v3 . should_equal [1] + + group_builder.specify "Should propagate dataflow errors" <| + v = Vector.build builder-> + builder.append 1 + builder.append 2 + Error.throw (Illegal_Argument.Error "asdf") + v . should_fail_with (Illegal_Argument.Error "asdf") + suite_builder.group "Vector/Array equality" group_builder-> v1 = [1, 2, 3] a1 = v1.to_array