Check type of self in static dispatch (#8867)

- Fixes #8805
- also btw. fixes #8706
This commit is contained in:
Radosław Waśko 2024-02-14 16:50:13 +01:00 committed by GitHub
parent ee381369b0
commit d45f0fe4df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 643 additions and 311 deletions

View File

@ -1046,6 +1046,7 @@
- [Binary operator resolution based on that value][8779]
- [Add run_google_report method][8907]
- [Execute and debug individual Enso files in VSCode extension][8923]
- [Check type of `self` when calling a method using the static syntax][8867]
[3227]: https://github.com/enso-org/enso/pull/3227
[3248]: https://github.com/enso-org/enso/pull/3248
@ -1203,6 +1204,7 @@
[8779]: https://github.com/enso-org/enso/pull/8779
[8907]: https://github.com/enso-org/enso/pull/8907
[8923]: https://github.com/enso-org/enso/pull/8923
[8867]: https://github.com/enso-org/enso/pull/8867
# Enso 2.0.0-alpha.18 (2021-10-12)

View File

@ -144,7 +144,7 @@ type Array
[My_Type.Value 'hello', 1].to_array.sort == [1, My_Type.Value 'hello'].to_array
sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Problem_Behavior -> Vector Any ! Incomparable_Values
sort self (order = Sort_Direction.Ascending) on=Nothing by=Nothing on_incomparable=Problem_Behavior.Ignore =
Vector.sort self order on by on_incomparable
Array_Like_Helpers.sort self order on by on_incomparable
## ALIAS first, last, sample, slice
GROUP Selections
@ -159,7 +159,8 @@ type Array
If a `Range`, the selection is specified by two indices, from and to.
@range Index_Sub_Range.default_widget
take : (Index_Sub_Range | Range | Integer) -> Vector Any
take self range=(Index_Sub_Range.First 1) = Vector.take self range
take self range=(Index_Sub_Range.First 1) =
Array_Like_Helpers.take self range
## ALIAS skip
GROUP Selections
@ -173,7 +174,8 @@ type Array
If a `Range`, the selection is specified by two indices, from and to.
@range Index_Sub_Range.default_widget
drop : (Index_Sub_Range | Range | Integer) -> Vector Any
drop self range=(Index_Sub_Range.First 1) = Vector.drop self range
drop self range=(Index_Sub_Range.First 1) =
Array_Like_Helpers.drop self range
## GROUP Calculations
Inserts the given item into the array at the given index.
@ -192,7 +194,8 @@ type Array
['a', 'b', 'c'].to_array.insert -1 'X' == ['a', 'b', 'X', 'c'].to_array
['a', 'b', 'c'].to_array.insert item='X' == ['a', 'b', 'c', 'X'].to_array
insert : Integer -> Any -> Vector ! Index_Out_Of_Bounds
insert self at=self.length item=Nothing = Vector.insert self at item
insert self at=self.length item=Nothing =
Array_Like_Helpers.insert self at item
## GROUP Selections
Removes the item at the given index from the array.
@ -202,7 +205,7 @@ type Array
If the index is less than 0, the index will be counted back from the
end.
remove : Integer -> Vector
remove self at=-1 = Vector.remove self at
remove self at=-1 = Array_Like_Helpers.remove self at
## GROUP Selections
ICON select_row
@ -262,7 +265,8 @@ type Array
["ab", "abab", "aba", "bbb"].to_array.index_of (s-> s == s.reverse) == 2
index_of : (Any | Filter_Condition | (Any -> Boolean)) -> Integer -> Integer | Nothing
index_of self condition start=0 = Vector.index_of self condition start
index_of self condition (start : Integer = 0) =
Array_Like_Helpers.index_of self condition start
## GROUP Values
Returns the last index of an element in the array.
@ -284,7 +288,8 @@ type Array
["ab", "abab", "aba", "bbb"].to_array.last_index_of (s-> s == s.reverse) == 3
last_index_of : (Any | Filter_Condition | (Any -> Boolean)) -> Integer -> Integer | Nothing
last_index_of self condition start=-1 = Vector.last_index_of self condition start
last_index_of self condition (start : Integer = -1) =
Array_Like_Helpers.last_index_of self condition start
## GROUP Logical
ICON metadata
@ -299,7 +304,7 @@ type Array
## Converts the array to a list with the same elements.
to_list : List
to_list self = Vector.to_list self
to_list self = Array_Like_Helpers.to_list self
## GROUP Selections
ICON preparation
@ -325,7 +330,7 @@ type Array
[Pair 1 "a", Pair 2 "b", Pair 1 "c"].to_array . distinct (on = _.first) == [Pair 1 "a", Pair 2 "b"].to_array
distinct : (Any -> Any) -> Vector Any
distinct self (on = x->x) = Vector.distinct self on
distinct self (on = x->x) = Array_Like_Helpers.distinct self on
## ICON dataframe_map_column
Applies a function to each element of the array, returning the `Vector` of
@ -363,7 +368,8 @@ type Array
[1, 2, 3].to_array . map +1
map : (Any -> Any) -> Problem_Behavior | No_Wrap -> Vector Any
map self function on_problems=Problem_Behavior.Report_Error = Vector.map self function on_problems
map self function on_problems=Problem_Behavior.Report_Error =
Array_Like_Helpers.map self function on_problems
## Applies a function to each element of the array, returning the `Vector`
that contains all results concatenated.
@ -400,7 +406,8 @@ type Array
[0, 1, 2].to_array . flat_map (n -> Vector.fill n n)
flat_map : (Any -> Vector Any) -> Problem_Behavior | No_Wrap -> Vector Any
flat_map self function on_problems=Problem_Behavior.Report_Error = Vector.flat_map self function on_problems
flat_map self function on_problems=Problem_Behavior.Report_Error =
Array_Like_Helpers.flat_map self function on_problems
## GROUP Selections
ICON preparation
@ -418,7 +425,7 @@ type Array
[1, 2, 3, 4, 5].to_array.filter (> 3)
[1, 2, 3, 4, 5].to_array.filter (Filter_Condition.Greater than=3)
filter : (Filter_Condition | (Any -> Boolean)) -> Vector Any
filter self filter = Vector.filter self filter
filter self filter = Array_Like_Helpers.filter self filter
## GROUP Calculations
Transforms an array of arrays into a `Vector` of inner elements - removes
@ -429,12 +436,13 @@ type Array
[[1, 2, 3].to_array, [4, 10].to_array, [].to_array, [0].to_array, [0].to_array].to_array . flatten == [1, 2, 3, 4, 10, 0, 0].to_array
flatten : Vector Any
flatten self = Vector.flatten self
flatten self = Array_Like_Helpers.flatten self
## PRIVATE
ADVANCED
short_display_text : Integer -> Text
short_display_text self max_entries=10 = Vector.short_display_text self max_entries
short_display_text self max_entries=10 =
Array_Like_Helpers.short_display_text self max_entries
## Combines all the elements of the array, by iteratively applying the
passed function with the next element of the array. After each step the
@ -449,7 +457,8 @@ type Array
[1, 2, 3].to_array.running_fold 0 (+)
running_fold : Any -> (Any -> Any -> Any) -> Vector Any
running_fold self init function = Vector.running_fold self init function
running_fold self init function =
Array_Like_Helpers.running_fold self init function
## ICON dataframe_map_column
Combines all the elements of the array, by iteratively applying the
@ -469,7 +478,8 @@ type Array
[0, 1, 2].to_array . fold 0 (+)
fold : Any -> (Any -> Any -> Any) -> Any
fold self init function = Vector.fold self init function
fold self init function =
Array_Like_Helpers.fold self init function
## ICON dataframe_map_column
Combines all the elements of the array, by iteratively applying the
@ -485,7 +495,8 @@ type Array
[0, 1, 2].to_array . fold_with_index 0 (s->i->e->s+i+e)
fold_with_index : Any -> (Any -> Integer -> Any -> Any) -> Any
fold_with_index self init function = Vector.fold_with_index self init function
fold_with_index self init function =
Array_Like_Helpers.fold_with_index self init function
## GROUP Calculations
Extend `self` array to the length of `n` appending elements `elem` to
@ -508,7 +519,7 @@ type Array
[1, 2, 3, 4, 5].to_array.pad 5 0 == [1, 2, 3, 4, 5].to_array
pad : Integer -> Any -> Vector Any
pad self n elem = Vector.pad self n elem
pad self n elem = Array_Like_Helpers.pad self n elem
## GROUP Selections
ICON split_text
@ -536,7 +547,8 @@ type Array
[1, 2, 3, 4, 5].to_array.partition (x -> x % 2 == 0) == (Pair [2, 4].to_array [1, 3, 5].to_array)
partition : (Filter_Condition | (Any -> Boolean)) -> Pair (Vector Any) (Vector Any)
partition self condition = Vector.partition self condition
partition self condition =
Array_Like_Helpers.partition self condition
## Partitions the array into `Vector`s of elements which satisfy a given
predicate and ones that do not.
@ -557,7 +569,8 @@ type Array
["a", "b", "c", "d"].to_array.partition_with_index (ix -> _ -> ix % 2 == 0) == (Pair ["a", "c"].to_array ["b", "d"].to_array)
partition_with_index : (Integer -> Any -> Boolean) -> Pair (Vector Any) (Vector Any)
partition_with_index self predicate = Vector.partition_with_index self predicate
partition_with_index self predicate =
Array_Like_Helpers.partition_with_index self predicate
## Applies a function to each element of the array, returning the `Vector`
of results.
@ -597,7 +610,8 @@ type Array
[1, 2, 3].to_array.map_with_index (+)
map_with_index : (Integer -> Any -> Any) -> Problem_Behavior | No_Wrap -> Vector Any
map_with_index self function on_problems=Problem_Behavior.Report_Error = Vector.map_with_index self function on_problems
map_with_index self function on_problems=Problem_Behavior.Report_Error =
Array_Like_Helpers.map_with_index self function on_problems
## PRIVATE
Creates a new array with the skipping elements until `start` and then
@ -612,7 +626,8 @@ type Array
[1, 2, 3, 4, 5, 6, 7, 8].to_array.slice 2 5 == [3, 4, 5].to_array
slice : Integer -> Integer -> Vector Any
slice self start end = Vector.slice self start end
slice self start end =
Array_Like_Helpers.slice self start end
## GROUP Selections
Returns the first element of the array that satisfies the condition or
@ -630,7 +645,8 @@ type Array
[1, 2, 3, 4, 5].to_array.find (> 3)
find : (Filter_Condition | (Any -> Boolean)) -> Integer -> Any -> Any
find self condition start=0 ~if_missing=(Error.throw Not_Found) = Vector.find self condition start if_missing
find self condition (start : Integer = 0) ~if_missing=(Error.throw Not_Found) =
Array_Like_Helpers.find self condition start if_missing
## ICON select_row
Gets an element from the array at a specified index (0-based).
@ -642,7 +658,8 @@ type Array
of the array, i.e. -1 will correspond to the last element.
- if_missing: The value to return if the index is out of bounds.
get : Integer -> Any -> Any
get self index ~if_missing=Nothing = Vector.get self index if_missing
get self index ~if_missing=Nothing =
Array_Like_Helpers.get self index if_missing
## GROUP Logical
ICON metadata
@ -669,7 +686,8 @@ type Array
[0, 10, 2, 2].to_array.filter (==) == [0, 2].to_array
filter_with_index : (Integer -> Any -> Boolean) -> Vector Any
filter_with_index self predicate = Vector.filter_with_index self predicate
filter_with_index self predicate =
Array_Like_Helpers.filter_with_index self predicate
## GROUP Calculations
ICON join
@ -686,7 +704,8 @@ type Array
["foo", "bar", "baz"].to_array.join ", "
join : Text -> Text -> Text -> Text
join self separator="" prefix="" suffix="" = Vector.join self separator prefix suffix
join self separator:Text="" prefix:Text="" suffix:Text="" =
Array_Like_Helpers.join self separator prefix suffix
## PRIVATE
Generates a human-readable text representation of the array.
@ -710,7 +729,8 @@ type Array
[0, 1, 2].to_array . reduce (+)
reduce : (Any -> Any -> Any) -> Any -> Any
reduce self function ~if_empty=(Error.throw (Empty_Error.Error Array)) = Vector.reduce self function if_empty
reduce self function ~if_empty=(Error.throw (Empty_Error.Error Array)) =
Array_Like_Helpers.reduce self function if_empty
## GROUP Logical
ICON preparation
@ -730,7 +750,7 @@ type Array
[1, 2, 3, 4, 5].to_array.any (x-> x%2 == 0)
any : (Filter_Condition | (Any -> Boolean)) -> Boolean
any self condition = Vector.any self condition
any self condition = Array_Like_Helpers.any self condition
## GROUP Logical
ICON preparation
@ -750,7 +770,7 @@ type Array
[-1, 1, 5, 8].to_array.all (x-> x%2 == 0)
all : (Filter_Condition | (Any -> Boolean)) -> Boolean
all self condition = Vector.all self condition
all self condition = Array_Like_Helpers.all self condition
## GROUP Logical
ICON preparation
@ -831,7 +851,7 @@ type Array
[1, 2].to_array.reverse
reverse : Vector Any
reverse self = Vector.reverse self
reverse self = Array_Like_Helpers.reverse self
## PRIVATE
ADVANCED
@ -848,7 +868,7 @@ type Array
[1, 2, 3, 4, 5].to_array . each IO.println
each : (Any -> Any) -> Nothing
each self f = Vector.each self f
each self f = Array_Like_Helpers.each self f
## PRIVATE
ADVANCED
@ -868,7 +888,7 @@ type Array
[1, 2, 3, 4, 5].to_array . each_with_index (ix->elem-> IO.println Pair ix elem)
each_with_index : (Integer -> Any -> Any) -> Nothing
each_with_index self f = Vector.each_with_index self f
each_with_index self f = Array_Like_Helpers.each_with_index self f
## ALIAS append, concatenate, union
GROUP Operators
@ -884,4 +904,4 @@ type Array
[1].to_array + [2].to_array
+ : Vector Any -> Vector Any
+ self that = Vector.+ self that
+ self that = Array_Like_Helpers.plus self that

View File

@ -555,7 +555,7 @@ type List
example_first = Examples.list.find (> 2)
find : (Filter_Condition | (Any -> Boolean)) -> Integer -> Any -> Any
find self condition start=0 ~if_missing=(Error.throw Not_Found) =
find self condition (start : Integer = 0) ~if_missing=(Error.throw Not_Found) =
predicate = unify_condition_or_predicate condition
case start.signum of
-1 ->
@ -583,7 +583,7 @@ type List
[1, 2, 3, 4, 5].find (> 3)
index_of : (Any | Filter_Condition | (Any -> Boolean)) -> Integer -> Integer | Nothing
index_of self condition start=0 = case start.signum of
index_of self condition (start : Integer = 0) = case start.signum of
-1 ->
node_and_index = find_node_from_end self start
found = node_and_index.first.index_of condition 0
@ -611,7 +611,7 @@ type List
[1, 2, 3, 4, 5].find (> 3)
last_index_of : (Any | Filter_Condition | (Any -> Boolean)) -> Integer -> Integer | Nothing
last_index_of self condition start=-1 = case self of
last_index_of self condition (start : Integer = -1) = case self of
Nil -> if start == -1 || start == 0 then Nothing else Error.throw (Index_Out_Of_Bounds.Error start 0)
Cons _ _ ->
length = self.length

View File

@ -147,7 +147,7 @@ type Pair
Pair.new 1 6 .find (> 3)
find : (Any -> Boolean) -> Integer -> Any -> Any
find self predicate start=0 ~if_missing=(Error.throw Not_Found) =
find self predicate (start : Integer = 0) ~if_missing=(Error.throw Not_Found) =
check_start_valid start used_start->
if used_start<1 && predicate self.first then self.first else
if used_start<2 && predicate self.second then self.second else
@ -169,7 +169,7 @@ type Pair
Pair.new 1 2 . index_of 2 == 1
Pair.new 2 2 . index_of 2 == 0
index_of : (Any | (Any -> Boolean)) -> Integer -> Integer | Nothing
index_of self element start=0 = check_start_valid start used_start->
index_of self element (start : Integer = 0) = check_start_valid start used_start->
predicate = case element of
_ : Function -> element
_ -> (==element)
@ -192,7 +192,7 @@ type Pair
Pair.new 2 2 . last_index_of 2 == 1
last_index_of : (Any | (Any -> Boolean)) -> Integer -> Integer | Nothing
last_index_of self element start=-1 = check_start_valid start max=2 used_start->
last_index_of self element (start : Integer = -1) = check_start_valid start max=2 used_start->
predicate = case element of
_ : Function -> element
_ -> (==element)

View File

@ -399,7 +399,7 @@ type Range
1.up_to 100 . find (Filter_Condition.Greater than=10)
@condition range_default_filter_condition_widget
find : (Filter_Condition | (Integer -> Boolean)) -> Integer -> Any -> Any
find self condition start=0 ~if_missing=Nothing =
find self condition (start : Integer = 0) ~if_missing=Nothing =
predicate = unify_condition_or_predicate condition
check_start_valid start self used_start->
result = find_internal self used_start predicate
@ -444,7 +444,7 @@ type Range
0.up_to 100 . with_step 5 . index_of (>10) == 3
@condition range_default_filter_condition_widget
index_of : (Integer | Filter_Condition | (Integer -> Boolean)) -> Integer -> Integer | Nothing
index_of self condition start=0 =
index_of self condition (start : Integer = 0) =
check_start_valid start self used_start->
case condition of
element : Integer -> get_index self used_start self.length-1 element
@ -471,7 +471,7 @@ type Range
Pair.new 2 2 . last_index_of 2 == 1
@condition range_default_filter_condition_widget
last_index_of : (Integer | Filter_Condition | (Integer -> Boolean)) -> Integer -> Integer | Nothing
last_index_of self condition start=-1 =
last_index_of self condition (start : Integer = -1) =
if self.is_empty && (start==-1 || start==0) then Nothing else
check_start_valid start self include_end=False used_start->
case condition of

View File

@ -1390,7 +1390,7 @@ Text.locate_all self term="" case_sensitivity=Case_Sensitivity.Sensitive = if te
"Hello World!".index_of "J" == Nothing
"Hello World!".index_of "o" == 4
Text.index_of : Text -> Integer -> Case_Sensitivity -> Integer | Nothing
Text.index_of self term="" start=0 case_sensitivity=Case_Sensitivity.Sensitive =
Text.index_of self term="" (start : Integer = 0) case_sensitivity=Case_Sensitivity.Sensitive =
used_start = if start < 0 then start+self.length else start
if used_start < 0 || used_start > self.length then Error.throw (Index_Out_Of_Bounds.Error start self.length+1) else
used = if used_start == 0 then self else self.drop used_start

View File

@ -401,7 +401,7 @@ type Date_Range
(Date.new 2020 10 01).up_to (Date.new 2020 10 31) . find (d-> d.day_of_week == Day_Of_Week.Monday)
@condition date_range_default_filter_condition_widget
find : (Filter_Condition | (Date -> Boolean)) -> Integer -> Any -> Any
find self condition start=0 ~if_missing=Nothing =
find self condition (start : Integer = 0) ~if_missing=Nothing =
predicate = unify_condition_or_predicate condition
index = self.index_of predicate start
case index of
@ -435,7 +435,7 @@ type Date_Range
(Date.new 2020 10 01).up_to (Date.new 2020 10 31) . index_of (d-> d.day_of_week == Day_Of_Week.Monday)
@condition date_range_default_filter_condition_widget
index_of : (Date | Filter_Condition | (Date -> Boolean)) -> Integer -> Integer | Nothing
index_of self condition start=0 =
index_of self condition (start : Integer = 0) =
predicate = unify_condition_for_index_of condition
(0.up_to self.length).index_of (ix-> predicate (self.internal_at ix)) start
@ -455,7 +455,7 @@ type Date_Range
(Date.new 2020 10 01).up_to (Date.new 2020 10 31) . last_index_of (d-> d.day_of_week == Day_Of_Week.Monday)
@condition date_range_default_filter_condition_widget
last_index_of : (Date | Filter_Condition | (Date -> Boolean)) -> Integer -> Integer | Nothing
last_index_of self condition start=-1 =
last_index_of self condition (start : Integer = -1) =
predicate = unify_condition_for_index_of condition
(0.up_to self.length).last_index_of (ix-> predicate (self.internal_at ix)) start

View File

@ -2,7 +2,6 @@ import project.Any.Any
import project.Data.Array.Array
import project.Data.Filter_Condition.Filter_Condition
import project.Data.List.List
import project.Data.Map.Map
import project.Data.Numbers.Integer
import project.Data.Pair.Pair
import project.Data.Range.Range
@ -27,7 +26,7 @@ import project.Panic.Panic
import project.Random.Random
from project.Data.Boolean import Boolean, False, True
from project.Data.Filter_Condition import unify_condition_or_predicate, unify_condition_predicate_or_element
from project.Data.Index_Sub_Range import drop_helper, Index_Sub_Range, take_helper
from project.Data.Index_Sub_Range import Index_Sub_Range
from project.Data.Ordering import all
from project.Data.Range.Extensions import all
@ -233,9 +232,7 @@ type Vector a
- if_missing: The value to return if the index is out of bounds.
get : Integer -> Any -> Any
get self index ~if_missing=Nothing =
len = self.length
if index < -len || index >= len then if_missing else
self.at index
Array_Like_Helpers.get self index if_missing
## ICON dataframe_map_column
Combines all the elements of the vector, by iteratively applying the
@ -256,8 +253,7 @@ type Vector a
[0, 1, 2] . fold 0 (+)
fold : Any -> (Any -> Any -> Any) -> Any
fold self init function =
f = acc -> ix -> function acc (self.at ix)
0.up_to self.length . fold init f
Array_Like_Helpers.fold self init function
## ICON dataframe_map_column
Combines all the elements of the vector, by iteratively applying the
@ -274,8 +270,7 @@ type Vector a
[0, 1, 2] . fold_with_index 0 (s->i->e->s+i+e)
fold_with_index : Any -> (Any -> Integer -> Any -> Any) -> Any
fold_with_index self init function =
f = acc -> ix -> function acc ix (self.at ix)
0.up_to self.length . fold init f
Array_Like_Helpers.fold_with_index self init function
## Combines all the elements of the vector, by iteratively applying the
passed function with the next element of the vector. After each step the
@ -291,11 +286,7 @@ type Vector a
[1, 2, 3].running_fold 0 (+)
running_fold : Any -> (Any -> Any -> Any) -> Vector Any
running_fold self init function =
wrapped builder value =
current = if builder.length == 0 then init else builder.last
builder.append (function current value)
built = self.fold (Vector.new_builder self.length) wrapped
built.to_vector
Array_Like_Helpers.running_fold self init function
## ICON dataframe_map_column
Combines all the elements of a non-empty vector using a binary operation.
@ -311,15 +302,7 @@ type Vector a
[0, 1, 2] . reduce (+)
reduce : (Any -> Any -> Any) -> Any -> Any
reduce self function ~if_empty=(Error.throw (Empty_Error.Error Vector)) =
len = self.length
case len of
0 -> if_empty
1 -> self.at 0
_ ->
fold_function current idx =
if idx == len then current else
@Tail_Call fold_function (function current (self.at idx)) (idx + 1)
fold_function (self.at 0) 1
Array_Like_Helpers.reduce self function if_empty
## GROUP Selections
Returns the first element of the vector that satisfies the condition or
@ -337,12 +320,8 @@ type Vector a
[1, 2, 3, 4, 5].find (> 3)
find : (Filter_Condition | (Any -> Boolean)) -> Integer -> Any -> Any
find self condition start=0 ~if_missing=(Error.throw Not_Found) =
predicate = unify_condition_or_predicate condition
self_len = self.length
check_start_valid start self_len used_start->
found = used_start.up_to self_len . find (idx -> (predicate (self.at idx)))
if found.is_nothing then if_missing else self.at found
find self condition (start : Integer = 0) ~if_missing=(Error.throw Not_Found) =
Array_Like_Helpers.find self condition start if_missing
## GROUP Values
Returns the index of an element in the vector.
@ -364,11 +343,8 @@ type Vector a
["ab", "abab", "aba", "bbb"].index_of (s-> s == s.reverse) == 2
index_of : (Any | Filter_Condition | (Any -> Boolean)) -> Integer -> Integer | Nothing
index_of self condition start=0 =
self_len = self.length
check_start_valid start self_len used_start->
predicate = unify_condition_predicate_or_element condition
used_start.up_to self.length . find if_missing=Nothing (i-> predicate (self.at i))
index_of self condition (start : Integer = 0) =
Array_Like_Helpers.index_of self condition start
## GROUP Values
Returns the last index of an element in the vector.
@ -390,12 +366,8 @@ type Vector a
["ab", "abab", "aba", "bbb"].last_index_of (s-> s == s.reverse) == 3
last_index_of : (Any | Filter_Condition | (Any -> Boolean)) -> Integer -> Integer | Nothing
last_index_of self condition start=-1 =
self_len = self.length
if self_len == 0 && (start==0 || start==-1) then Nothing else
check_start_valid start self_len used_start->
predicate = unify_condition_predicate_or_element condition
used_start.down_to -1 . find if_missing=Nothing (i-> predicate (self.at i))
last_index_of self condition (start : Integer = -1) =
Array_Like_Helpers.last_index_of self condition start
## GROUP Logical
ICON preparation
@ -416,8 +388,7 @@ type Vector a
[1, 2, 3, 4, 5].any (x-> x%2 == 0)
any : (Filter_Condition | (Any -> Boolean)) -> Boolean
any self condition =
predicate = unify_condition_or_predicate condition
0.up_to self.length . any (idx -> (predicate (self.at idx)))
Array_Like_Helpers.any self condition
## GROUP Logical
ICON preparation
@ -438,8 +409,7 @@ type Vector a
[-1, 1, 5, 8].all (x-> x%2 == 0)
all : (Filter_Condition | (Any -> Boolean)) -> Boolean
all self condition =
predicate = unify_condition_or_predicate condition
self.any (predicate >> .not) . not
Array_Like_Helpers.all self condition
## GROUP Logical
ICON preparation
@ -494,10 +464,7 @@ type Vector a
[1, 2, 3, 4, 5].filter (Filter_Condition.Greater than=3)
filter : (Filter_Condition | (Any -> Boolean)) -> Vector Any
filter self filter =
predicate = unify_condition_or_predicate filter
builder = self.fold Vector.new_builder builder-> elem->
if predicate elem then builder.append elem else builder
builder.to_vector
Array_Like_Helpers.filter self filter
## GROUP Selections
ICON preparation
@ -514,9 +481,7 @@ type Vector a
[0, 10, 2, 2].filter (==) == [0, 2]
filter_with_index : (Integer -> Any -> Boolean) -> Vector Any
filter_with_index self predicate =
builder = self.fold_with_index Vector.new_builder builder-> ix-> elem->
if predicate ix elem then builder.append elem else builder
builder.to_vector
Array_Like_Helpers.filter_with_index self predicate
## GROUP Selections
ICON split_text
@ -545,14 +510,7 @@ type Vector a
[1, 2, 3, 4, 5].partition (x -> x % 2 == 0) == (Pair [2, 4] [1, 3, 5])
partition : (Filter_Condition | (Any -> Boolean)) -> Pair (Vector Any) (Vector Any)
partition self condition =
predicate = unify_condition_or_predicate condition
pair = self.fold (Pair.new Vector.new_builder Vector.new_builder) acc-> elem->
case predicate elem of
True ->
Pair.new (acc.first.append elem) acc.second
False ->
Pair.new acc.first (acc.second.append elem)
pair.map .to_vector
Array_Like_Helpers.partition self condition
## Partitions the vector into `Vector`s of elements which satisfy a given
predicate and ones that do not.
@ -574,11 +532,7 @@ type Vector a
["a", "b", "c", "d"].partition_with_index (ix -> _ -> ix % 2 == 0) == (Pair ["a", "c"] ["b", "d"])
partition_with_index : (Integer -> Any -> Boolean) -> Pair (Vector Any) (Vector Any)
partition_with_index self predicate =
pair = self.fold_with_index (Pair.new Vector.new_builder Vector.new_builder) acc-> ix-> elem->
case predicate ix elem of
True -> Pair.new (acc.first.append elem) acc.second
False -> Pair.new acc.first (acc.second.append elem)
pair.map .to_vector
Array_Like_Helpers.partition_with_index self predicate
## ICON dataframe_map_column
Applies a function to each element of the vector, returning the `Vector` of
@ -617,7 +571,7 @@ type Vector a
[1, 2, 3] . map +1
map : (Any -> Any) -> Problem_Behavior | No_Wrap -> Vector Any
map self function on_problems=Problem_Behavior.Report_Error =
Array_Like_Helpers.vector_from_function self.length (function << self.at) on_problems
Array_Like_Helpers.map self function on_problems
## Applies a function to each element of the vector, returning the `Vector`
that contains all results concatenated.
@ -655,7 +609,7 @@ type Vector a
[0, 1, 2] . flat_map (n -> Vector.fill n n)
flat_map : (Any -> Vector Any) -> Problem_Behavior | No_Wrap -> Vector Any
flat_map self function on_problems=Problem_Behavior.Report_Error =
self.map function on_problems . flatten
Array_Like_Helpers.flat_map self function on_problems
## GROUP Calculations
Transforms a vector of vectors into a `Vector` of inner elements - removes
@ -666,7 +620,7 @@ type Vector a
[[1, 2, 3], [4, 10], [], [0], [0]] . flatten == [1, 2, 3, 4, 10, 0, 0]
flatten : Vector Any
flatten self = @Builtin_Method "Vector.flatten"
flatten self = Array_Like_Helpers.flatten self
## Applies a function to each element of the vector, returning the `Vector`
of results.
@ -707,7 +661,7 @@ type Vector a
[1, 2, 3].map_with_index (+)
map_with_index : (Integer -> Any -> Any) -> Problem_Behavior | No_Wrap -> Vector Any
map_with_index self function on_problems=Problem_Behavior.Report_Error =
Array_Like_Helpers.vector_from_function self.length (i-> function i (self.at i)) on_problems
Array_Like_Helpers.map_with_index self function on_problems
## PRIVATE
ADVANCED
@ -725,8 +679,7 @@ type Vector a
[1, 2, 3, 4, 5] . each IO.println
each : (Any -> Any) -> Nothing
each self f =
0.up_to self.length . each ix->
f (self.at ix)
Array_Like_Helpers.each self f
## PRIVATE
ADVANCED
@ -747,8 +700,7 @@ type Vector a
[1, 2, 3, 4, 5] . each_with_index (ix->elem-> IO.println Pair ix elem)
each_with_index : (Integer -> Any -> Any) -> Nothing
each_with_index self f =
0.up_to self.length . each ix->
f ix (self.at ix)
Array_Like_Helpers.each_with_index self f
## GROUP Selections
Reverses the vector, returning a `Vector` with the same elements, but in
@ -759,7 +711,8 @@ type Vector a
[1, 2].reverse
reverse : Vector Any
reverse self = Vector.new self.length (i -> self.at (self.length - (1 + i)))
reverse self =
Array_Like_Helpers.reverse self
## PRIVATE
Generates a human-readable text representation of the vector.
@ -786,14 +739,8 @@ type Vector a
(0.up_to 100).to_vector.short_display_text max_entries=2 == "[0, 1 and 98 more elements]"
short_display_text : Integer -> Text
short_display_text self max_entries=10 =
if max_entries < 1 then Error.throw <| Illegal_Argument.Error "The `max_entries` parameter must be positive." else
prefix = self.take (Index_Sub_Range.First max_entries)
if prefix.length == self.length then self.to_text else
remaining_count = self.length - prefix.length
remaining_text = if remaining_count == 1 then "and 1 more element" else
"and " + remaining_count.to_text + " more elements"
prefix.map .to_text . join ", " "[" " "+remaining_text+"]"
short_display_text self (max_entries : Integer = 10) =
Array_Like_Helpers.short_display_text self max_entries
## ALIAS append, concatenate, union
GROUP Operators
@ -809,9 +756,8 @@ type Vector a
[1] + [2]
+ : Vector Any | Array -> Vector Any
+ self that:(Vector | Array) = case that of
_ : Vector -> Vector.insert_builtin self self.length that
_ : Array -> self + Vector.from_polyglot_array that
+ self that:(Vector | Array) =
Array_Like_Helpers.plus self that
## GROUP Calculations
Inserts the given item into the vector at the given index.
@ -831,15 +777,7 @@ type Vector a
['a', 'b', 'c'].insert item='X' == ['a', 'b', 'c', 'X']
insert : Integer -> Any -> Vector ! Index_Out_Of_Bounds
insert self at=self.length item=Nothing =
self_len = self.length
used_index = if at < 0 then self_len + at else at
if used_index < 0 || used_index > self_len then Error.throw (Index_Out_Of_Bounds.Error at self_len+1) else
if used_index == self_len then self + [item] else
if used_index == 0 then [item] + self else
Vector.insert_builtin self used_index [item]
## PRIVATE
insert_builtin vec at values = @Builtin_Method("Vector.insert_builtin")
Array_Like_Helpers.insert self at item
## GROUP Selections
Removes the item at the given index from the vector.
@ -850,13 +788,7 @@ type Vector a
end.
remove : Integer -> Vector
remove self at=-1 =
self_len = self.length
used_index = if at < 0 then self_len + at else at
if used_index >= self_len || used_index < 0 then Error.throw (Index_Out_Of_Bounds.Error at self_len) else
Vector.remove_builtin self used_index
## PRIVATE
remove_builtin vec at = @Builtin_Method "Vector.remove_builtin"
Array_Like_Helpers.remove self at
## GROUP Calculations
ICON join
@ -873,10 +805,8 @@ type Vector a
["foo", "bar", "baz"].join ", "
join : Text -> Text -> Text -> Text
join self separator="" prefix="" suffix="" =
if self.is_empty then prefix+suffix else
if self.length == 1 then prefix + self.at 0 + suffix else
prefix + self.at 0 + (1.up_to self.length . fold "" acc-> i-> acc + separator + self.at i) + suffix
join self separator:Text="" prefix:Text="" suffix:Text="" =
Array_Like_Helpers.join self separator prefix suffix
## PRIVATE
Creates a new vector with the skipping elements until `start` and then
@ -891,7 +821,7 @@ type Vector a
[1, 2, 3, 4, 5, 6, 7, 8].slice 2 5 == [3, 4, 5]
slice : Integer -> Integer -> Vector Any
slice self start end = @Builtin_Method "Vector.slice"
slice self start end = Array_Like_Helpers.slice self start end
## ALIAS first, last, sample, slice
GROUP Selections
@ -906,16 +836,8 @@ type Vector a
If a `Range`, the selection is specified by two indices, from and to.
@range Index_Sub_Range.default_widget
take : (Index_Sub_Range | Range | Integer) -> Vector Any
take self range=(Index_Sub_Range.First 1) = case range of
## We are using a specialized implementation for `take Sample`, because
the default implementation (which needs to be generic for any
collection) generates a random set of indices and then selects these
indices, but we can sample the vector directly.
Index_Sub_Range.Sample count seed ->
rng = Random.new_generator seed
rng.items self count
_ ->
take_helper self.length (self.at _) self.slice (slice_ranges self) range
take self range=(Index_Sub_Range.First 1) =
Array_Like_Helpers.take self range
## ALIAS skip
GROUP Selections
@ -930,7 +852,7 @@ type Vector a
@range Index_Sub_Range.default_widget
drop : (Index_Sub_Range | Range | Integer) -> Vector Any
drop self range=(Index_Sub_Range.First 1) =
drop_helper self.length (self.at _) self.slice (slice_ranges self) range
Array_Like_Helpers.drop self range
## ALIAS combine, join by row position, merge
GROUP Calculations
@ -1006,8 +928,7 @@ type Vector a
[1, 2, 3, 4, 5].pad 5 0 == [1, 2, 3, 4, 5]
pad : Integer -> Any -> Vector Any
pad self n elem =
if self.length >= n then self else
self + (Vector.fill n-self.length elem)
Array_Like_Helpers.pad self n elem
## GROUP Selections
ICON select_row
@ -1124,11 +1045,7 @@ type Vector a
[My_Type.Value 'hello', 1].sort == [1, My_Type.Value 'hello']
sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Problem_Behavior -> Vector Any ! Incomparable_Values
sort self (order = Sort_Direction.Ascending) on=Nothing by=Nothing on_incomparable=Problem_Behavior.Ignore =
comps = case on == Nothing of
True -> self.map it-> Comparable.from it
False -> self.map it-> Comparable.from (on it)
compare_funcs = comps.map (it-> it.compare)
self.sort_builtin order.to_sign comps compare_funcs by on on_incomparable.to_number
Array_Like_Helpers.sort self order on by on_incomparable
## GROUP Selections
ICON preparation
@ -1155,14 +1072,7 @@ type Vector a
[Pair 1 "a", Pair 2 "b", Pair 1 "c"] . distinct (on = _.first) == [Pair 1 "a", Pair 2 "b"]
distinct : (Any -> Any) -> Vector Any
distinct self (on = x->x) =
builder = Vector.new_builder
result = self.fold Map.empty existing->
item->
key = on item
if (existing.get key False) then existing else
builder.append item
existing.insert key True
if result.is_error then result else builder.to_vector
Array_Like_Helpers.distinct self on
## Returns the vector as a `Vector`.
to_vector : Vector
@ -1170,8 +1080,7 @@ type Vector a
## Converts the vector to a list with the same elements.
to_list : List
to_list self =
self.reverse.fold List.Nil acc-> elem-> List.Cons elem acc
to_list self = Array_Like_Helpers.to_list self
## PRIVATE
type Builder
@ -1270,7 +1179,7 @@ type Builder
builder = Vector.new_builder
builder . append_vector_range [20, 30, 40, 50] 1 3 . to_vector == [30, 40]
append_vector_range : Vector Any ! Error -> Integer -> Integer -> Builder ! Error
append_vector_range self vector start=0 end=vector.length =
append_vector_range self vector (start : Integer = 0) end=vector.length =
subrange = vector.slice start end
## This workaround is needed because
`self.java_builder.addAll subrange.to_array` fails with
@ -1393,42 +1302,3 @@ type No_Wrap
## PRIVATE
Wrapped_Error.from (that : Map_Error) = Wrapped_Error.Value that that.inner_error
## PRIVATE
Creates a new vector where for each range, a corresponding section of the
source vector is added to the result.
Assumes that the ranges have been already bounds-checked (for example by
passing them through `resolve_ranges`).
slice_ranges vector ranges =
if ranges.length == 0 then [] else
if ranges.length != 1 then slice_many_ranges vector ranges else
case ranges.first of
_ : Integer -> [vector.at ranges.first]
Range.Between start end step -> case step == 1 of
True -> vector.slice start end
False -> slice_many_ranges vector ranges
## PRIVATE
See `slice_ranges`.
slice_many_ranges vector ranges =
new_length = ranges.fold 0 acc-> descriptor-> case descriptor of
_ : Integer -> acc+1
_ : Range -> acc+descriptor.length
builder = Vector.new_builder new_length
ranges.each descriptor-> case descriptor of
_ : Integer ->
builder.append (vector.at descriptor)
Range.Between start end step -> case step == 1 of
True ->
builder.append_vector_range vector start end
False ->
descriptor.each ix->
builder.append (vector.at ix)
builder.to_vector
## PRIVATE
check_start_valid start length function =
used_start = if start < 0 then start + length else start
if used_start < 0 || used_start > length then Error.throw (Index_Out_Of_Bounds.Error start length+1) else
function used_start

View File

@ -2,45 +2,62 @@ private
import project.Any.Any
import project.Data.Array.Array
import project.Data.List.List
import project.Data.Map.Map
import project.Data.Maybe.Maybe
import project.Data.Numbers.Integer
import project.Data.Pair.Pair
import project.Data.Range.Range
import project.Data.Vector.Map_Error
import project.Data.Vector.No_Wrap
import project.Data.Vector.Vector
import project.Error.Error
import project.Errors.Common.Additional_Warnings
import project.Errors.Common.Index_Out_Of_Bounds
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Errors.Problem_Behavior.Problem_Behavior
import project.Errors.Unimplemented.Unimplemented
import project.Nothing.Nothing
import project.Random.Random
import project.Runtime
import project.Runtime.Ref.Ref
import project.Warning.Warning
from project.Data.Boolean import Boolean, False, True
from project.Data.Filter_Condition import unify_condition_or_predicate, unify_condition_predicate_or_element
from project.Data.Index_Sub_Range import Index_Sub_Range, drop_helper, take_helper
from project.Data.Ordering import Comparable
from project.Data.Range.Extensions import all
## PRIVATE
new_array_proxy_builtin : Integer -> (Integer -> Any) -> Array
new_array_proxy_builtin length at = @Builtin_Method "Array_Like_Helpers.new_array_proxy_builtin"
## PRIVATE
new_vector_builder : Integer -> Any
new_vector_builder capacity = @Builtin_Method "Array_Like_Helpers.new_vector_builder"
## PRIVATE
length : (Array | Vector) -> Integer
length array_like = @Builtin_Method "Array_Like_Helpers.length"
## PRIVATE
at : (Array | Vector) -> Integer -> Any
at array_like index = @Builtin_Method "Array_Like_Helpers.at"
## PRIVATE
vector_to_array : (Vector | Array) -> Array
vector_to_array array_like = @Builtin_Method "Array_Like_Helpers.vector_to_array"
## PRIVATE
vector_from_function_primitive : Integer -> (Integer -> Any) -> Vector Any
vector_from_function_primitive length constructor = @Builtin_Method "Array_Like_Helpers.vector_from_function"
flatten : (Vector | Array) -> Vector
flatten array_like = @Builtin_Method "Array_Like_Helpers.flatten"
insert_builtin : (Vector | Array) -> Integer -> (Vector | Array) -> Vector
insert_builtin vector at items = @Builtin_Method "Array_Like_Helpers.insert_builtin"
remove_builtin : (Vector | Array) -> Integer -> Vector
remove_builtin vector at = @Builtin_Method "Array_Like_Helpers.remove_builtin"
slice : (Vector | Array) -> Integer -> Integer -> Vector
slice vector start end = @Builtin_Method "Array_Like_Helpers.slice"
## PRIVATE
Construct a Vector by mapping a function over 0..length-1.
@ -92,3 +109,217 @@ vector_from_function length function on_problems=Problem_Behavior.Report_Error =
The maximum number of warnings attached to result values in
`vector_from_function`.
MAX_MAP_WARNINGS = 10
## PRIVATE
Creates a new vector where for each range, a corresponding section of the
source vector is added to the result.
Assumes that the ranges have been already bounds-checked (for example by
passing them through `resolve_ranges`).
slice_ranges vector ranges =
if ranges.length == 0 then [] else
if ranges.length != 1 then slice_many_ranges vector ranges else
case ranges.first of
_ : Integer -> [vector.at ranges.first]
Range.Between start end step -> case step == 1 of
True -> vector.slice start end
False -> slice_many_ranges vector ranges
## PRIVATE
See `slice_ranges`.
slice_many_ranges vector ranges =
new_length = ranges.fold 0 acc-> descriptor-> case descriptor of
_ : Integer -> acc+1
_ : Range -> acc+descriptor.length
builder = Vector.new_builder new_length
ranges.each descriptor-> case descriptor of
_ : Integer ->
builder.append (vector.at descriptor)
Range.Between start end step -> case step == 1 of
True ->
builder.append_vector_range vector start end
False ->
descriptor.each ix->
builder.append (vector.at ix)
builder.to_vector
check_start_valid start length function =
used_start = if start < 0 then start + length else start
if used_start < 0 || used_start > length then Error.throw (Index_Out_Of_Bounds.Error start length+1) else
function used_start
sort vector order on by on_incomparable =
comps = case on == Nothing of
True -> vector.map it-> Comparable.from it
False -> vector.map it-> Comparable.from (on it)
compare_funcs = comps.map (it-> it.compare)
vector.sort_builtin order.to_sign comps compare_funcs by on on_incomparable.to_number
distinct vector on =
builder = Vector.new_builder
result = vector.fold Map.empty existing->
item->
key = on item
if (existing.get key False) then existing else
builder.append item
existing.insert key True
if result.is_error then result else builder.to_vector
take vector range = case range of
## We are using a specialized implementation for `take Sample`, because
the default implementation (which needs to be generic for any
collection) generates a random set of indices and then selects these
indices, but we can sample the vector directly.
Index_Sub_Range.Sample count seed ->
rng = Random.new_generator seed
rng.items vector count
_ ->
take_helper vector.length (vector.at _) vector.slice (slice_ranges vector) range
drop vector range =
drop_helper vector.length (vector.at _) vector.slice (slice_ranges vector) range
get vector index ~if_missing =
len = vector.length
if index < -len || index >= len then if_missing else
vector.at index
insert vector at item =
self_len = vector.length
used_index = if at < 0 then self_len + at else at
if used_index < 0 || used_index > self_len then Error.throw (Index_Out_Of_Bounds.Error at self_len+1) else
if used_index == self_len then vector + [item] else
if used_index == 0 then [item] + vector else
insert_builtin vector used_index [item]
remove vector at =
self_len = vector.length
used_index = if at < 0 then self_len + at else at
if used_index >= self_len || used_index < 0 then Error.throw (Index_Out_Of_Bounds.Error at self_len) else
remove_builtin vector used_index
index_of vector condition start =
self_len = vector.length
check_start_valid start self_len used_start->
predicate = unify_condition_predicate_or_element condition
used_start.up_to vector.length . find if_missing=Nothing (i-> predicate (vector.at i))
last_index_of vector condition start =
self_len = vector.length
if self_len == 0 && (start==0 || start==-1) then Nothing else
check_start_valid start self_len used_start->
predicate = unify_condition_predicate_or_element condition
used_start.down_to -1 . find if_missing=Nothing (i-> predicate (vector.at i))
any vector condition =
predicate = unify_condition_or_predicate condition
0.up_to vector.length . any (idx -> (predicate (vector.at idx)))
all vector condition =
predicate = unify_condition_or_predicate condition
vector.any (predicate >> .not) . not
plus vector that:(Vector | Array) = case that of
_ : Vector -> insert_builtin vector vector.length that
_ : Array -> plus vector (Vector.from_polyglot_array that)
find vector condition start ~if_missing =
predicate = unify_condition_or_predicate condition
self_len = vector.length
check_start_valid start self_len used_start->
found = used_start.up_to self_len . find (idx -> (predicate (vector.at idx)))
if found.is_nothing then if_missing else vector.at found
map vector function on_problems =
vector_from_function vector.length (function << vector.at) on_problems
map_with_index vector function on_problems =
vector_from_function vector.length (i-> function i (vector.at i)) on_problems
flat_map vector function on_problems =
vector.map function on_problems . flatten
fold vector init function =
f = acc -> ix -> function acc (vector.at ix)
0.up_to vector.length . fold init f
fold_with_index vector init function =
f = acc -> ix -> function acc ix (vector.at ix)
0.up_to vector.length . fold init f
reduce vector function ~if_empty =
len = vector.length
case len of
0 -> if_empty
1 -> vector.at 0
_ ->
fold_function current idx =
if idx == len then current else
@Tail_Call fold_function (function current (vector.at idx)) (idx + 1)
fold_function (vector.at 0) 1
running_fold vector init function =
wrapped builder value =
current = if builder.length == 0 then init else builder.last
builder.append (function current value)
built = vector.fold (Vector.new_builder vector.length) wrapped
built.to_vector
pad vector n elem =
if vector.length >= n then vector else
vector + (Vector.fill n-vector.length elem)
each vector f =
0.up_to vector.length . each ix->
f (vector.at ix)
each_with_index vector f =
0.up_to vector.length . each ix->
f ix (vector.at ix)
reverse vector = Vector.new vector.length (i -> vector.at (vector.length - (1 + i)))
to_list vector =
vector.reverse.fold List.Nil acc-> elem-> List.Cons elem acc
short_display_text vector max_entries =
if max_entries < 1 then Error.throw <| Illegal_Argument.Error "The `max_entries` parameter must be positive." else
prefix = vector.take (Index_Sub_Range.First max_entries)
if prefix.length == vector.length then vector.to_text else
remaining_count = vector.length - prefix.length
remaining_text = if remaining_count == 1 then "and 1 more element" else
"and " + remaining_count.to_text + " more elements"
prefix.map .to_text . join ", " "[" " "+remaining_text+"]"
join vector separator prefix suffix =
if vector.is_empty then prefix+suffix else
if vector.length == 1 then prefix + vector.at 0 + suffix else
prefix + vector.at 0 + (1.up_to vector.length . fold "" acc-> i-> acc + separator + vector.at i) + suffix
partition vector condition =
predicate = unify_condition_or_predicate condition
pair = vector.fold (Pair.new Vector.new_builder Vector.new_builder) acc-> elem->
case predicate elem of
True ->
Pair.new (acc.first.append elem) acc.second
False ->
Pair.new acc.first (acc.second.append elem)
pair.map .to_vector
partition_with_index vector predicate =
pair = vector.fold_with_index (Pair.new Vector.new_builder Vector.new_builder) acc-> ix-> elem->
case predicate ix elem of
True -> Pair.new (acc.first.append elem) acc.second
False -> Pair.new acc.first (acc.second.append elem)
pair.map .to_vector
filter vector filter =
predicate = unify_condition_or_predicate filter
builder = vector.fold Vector.new_builder builder-> elem->
if predicate elem then builder.append elem else builder
builder.to_vector
filter_with_index vector predicate =
builder = vector.fold_with_index Vector.new_builder builder-> ix-> elem->
if predicate ix elem then builder.append elem else builder
builder.to_vector

View File

@ -50,11 +50,11 @@ type Warning
Arguments:
- warning_type: The type to throw if attached to the value. Defaults to all warnings.
throw_on_warning : Any -> Any
throw_on_warning self warning_type=Any =
warnings = Warning.get_all self
throw_on_warning : Any -> Any -> Any
throw_on_warning value warning_type=Any =
warnings = Warning.get_all value
first = warnings.find (w-> w.value.is_a warning_type) if_missing=Nothing
if first.is_nothing then self else Error.throw first.value
if first.is_nothing then value else Error.throw first.value
## PRIVATE
ADVANCED

View File

@ -998,6 +998,13 @@ object BindingsMap {
s"The name `${originalName.name}` could not be found"
}
/** A resolution error due to usage of Self type reference outside of a type scope.
*/
case object SelfTypeOutsideOfTypeDefinition extends ResolutionError {
override def explain(originalName: ir.Name): String =
s"The Self type is not applicable outside of a type definition"
}
/** A metadata-friendly storage for resolutions */
case class Resolution(target: ResolvedName) extends IRPass.IRMetadata {

View File

@ -100,19 +100,24 @@ case object MethodDefinitions extends IRPass {
case Some(Resolution(ResolvedType(_, tp)))
if canGenerateStaticWrappers(tp) =>
val dup = method.duplicate()
val static = dup.copy(body =
new Function.Lambda(
List(
DefinitionArgument
.Specified(
Name.Self(None, true),
// This is the self argument that will receive the `SelfType.type` value upon dispatch, it is added to avoid modifying the dispatch mechanism.
val syntheticModuleSelfArg = DefinitionArgument.Specified(
Name.Self(None, synthetic = true),
None,
None,
false,
suspended = false,
None
)
),
dup.body,
// The actual `self` argument that is referenced inside of method body is the second one in the lambda.
// This is the argument that will hold the actual instance of the object we are calling on, e.g. `My_Type.method instance`.
// We add a type check to it to ensure only `instance` of `My_Type` can be passed to it.
val static = dup.copy(body =
new Function.Lambda(
// This is the synthetic Self argument that gets the static module
List(syntheticModuleSelfArg),
// Here we add the type ascription ensuring that the 'proper' self argument only accepts _instances_ of the type (or triggers conversions)
addTypeAscriptionToSelfArgument(dup.body),
None
)
)
@ -127,11 +132,43 @@ case object MethodDefinitions extends IRPass {
ir.copy(bindings = withStaticAliases)
}
private def addTypeAscriptionToSelfArgument(
methodBody: Expression
): Expression = methodBody match {
case lambda: Function.Lambda =>
val newArguments = lambda.arguments match {
case (selfArg @ DefinitionArgument.Specified(
Name.Self(_, _, _, _),
_,
_,
_,
_,
_,
_
)) :: rest =>
val selfType = Name.SelfType(location = selfArg.location)
selfArg.copy(ascribedType = Some(selfType)) :: rest
case other :: _ =>
throw new CompilerError(
s"MethodDefinitions pass: expected the first argument to be `self`, but got $other"
)
case Nil =>
throw new CompilerError(
s"MethodDefinitions pass: expected at least one argument (self) in the method, but got none."
)
}
lambda.copy(arguments = newArguments)
case other =>
throw new CompilerError(
s"Unexpected body type $other in MethodDefinitions pass."
)
}
// Generate static wrappers for
// 1. Types having at least one type constructor
// 2. All builtin types except for Nothing. Nothing's eigentype is Nothing and not Nothing.type,
// would lead to overriding conflicts.
// TODO: Remvoe the hardcoded type once Enso's annotations can define parameters.
// TODO: Remove the hardcoded type once Enso's annotations can define parameters.
private def canGenerateStaticWrappers(tp: Type): Boolean =
tp.members.nonEmpty || (tp.builtinType && (tp.name != "Nothing"))

View File

@ -2,13 +2,12 @@ package org.enso.compiler.pass.resolve
import org.enso.compiler.context.{InlineContext, ModuleContext}
import org.enso.compiler.core.Implicits.AsMetadata
import org.enso.compiler.core.IR
import org.enso.compiler.core.ir.{Expression, Function, Module, Name}
import org.enso.compiler.core.ir.MetadataStorage.MetadataPair
import org.enso.compiler.core.ir.expression.errors
import org.enso.compiler.core.ir.module.scope.Definition
import org.enso.compiler.core.ir.module.scope.definition.Method
import org.enso.compiler.core.ir.MetadataStorage.MetadataPair
import org.enso.compiler.core.ir.`type`
import org.enso.compiler.core.ir.{`type`, Expression, Function, Module, Name}
import org.enso.compiler.core.{CompilerError, IR}
import org.enso.compiler.data.BindingsMap
import org.enso.compiler.data.BindingsMap.{Resolution, ResolvedModule}
import org.enso.compiler.pass.IRPass
@ -47,43 +46,81 @@ case object TypeNames extends IRPass {
val bindingsMap =
ir.unsafeGetMetadata(BindingAnalysis, "bindings analysis did not run")
ir.copy(bindings = ir.bindings.map { d =>
val typeParams: List[Name] = d match {
case t: Definition.Type => t.params.map(_.name)
val selfTypeInfo: SelfTypeInfo = d match {
case t: Definition.Type => SelfTypeInfo.fromTypeDefinition(t)
case m: Method.Explicit =>
val params: List[Name] = m.methodReference.typePointer
.flatMap { p =>
p.getMetadata(MethodDefinitions)
.map(_.target match {
case typ: BindingsMap.ResolvedType =>
typ.tp.params.map(Name.Literal(_, false, None)).toList
case _ => List()
})
}
.getOrElse(List())
params
case _ => Nil
SelfTypeInfo.fromMethodReference(m.methodReference)
case _ => SelfTypeInfo.empty
}
val mapped =
d.mapExpressions(resolveExpression(typeParams, bindingsMap, _))
doResolveType(
typeParams,
bindingsMap,
mapped match {
d.mapExpressions(
resolveExpression(selfTypeInfo, bindingsMap, _)
)
val withResolvedArguments = mapped match {
case typ: Definition.Type =>
typ.members.foreach(m =>
m.arguments.foreach(a =>
doResolveType(typ.params.map(_.name), bindingsMap, a)
doResolveType(
SelfTypeInfo.fromTypeDefinition(typ),
bindingsMap,
a
)
)
)
typ
case x => x
}
)
doResolveType(selfTypeInfo, bindingsMap, withResolvedArguments)
})
}
private case class SelfTypeInfo(
selfType: Option[BindingsMap.ResolvedType],
typeParams: List[Name]
)
private case object SelfTypeInfo {
def empty: SelfTypeInfo = SelfTypeInfo(None, Nil)
def fromTypeDefinition(d: Definition.Type): SelfTypeInfo = {
// TODO currently the `Self` type is only used internally as an ascription for static method bindings
// Once we actually start supporting the `Self` syntax, we should set the self type here to the ResolvedType
// corresponding to the current definition, so that we can correctly resolve `Self` references in constructor
// argument types.
val selfType = None
val typeParams = d.params.map(_.name)
SelfTypeInfo(selfType, typeParams)
}
def fromMethodReference(m: Name.MethodReference): SelfTypeInfo =
m.typePointer match {
case Some(p) =>
p.getMetadata(MethodDefinitions) match {
case Some(resolution) =>
resolution.target match {
case typ: BindingsMap.ResolvedType =>
val params =
typ.tp.params.map(Name.Literal(_, false, None)).toList
SelfTypeInfo(Some(typ), params)
case _: BindingsMap.ResolvedModule =>
SelfTypeInfo.empty
case other =>
throw new CompilerError(
s"Method target not resolved as ResolvedType, but $other."
)
}
case None =>
// It is unexpected that the metadata is missing here, but we don't fail because other passes should fail
// with more detailed info.
SelfTypeInfo.empty
}
case None => SelfTypeInfo.empty
}
}
private def resolveExpression(
typeParams: List[Name],
selfTypeInfo: SelfTypeInfo,
bindingsMap: BindingsMap,
ir: Expression
): Expression = {
@ -91,17 +128,23 @@ case object TypeNames extends IRPass {
val processedIr = ir match {
case fn: Function.Lambda =>
fn.copy(arguments =
fn.arguments.map(doResolveType(typeParams, bindingsMap, _))
fn.arguments.map(
doResolveType(selfTypeInfo, bindingsMap, _)
)
)
case x => x
}
doResolveType(typeParams, bindingsMap, processedIr.mapExpressions(go))
doResolveType(
selfTypeInfo,
bindingsMap,
processedIr.mapExpressions(go)
)
}
go(ir)
}
private def doResolveType[T <: IR](
typeParams: List[Name],
selfTypeInfo: SelfTypeInfo,
bindingsMap: BindingsMap,
ir: T
): T = {
@ -111,7 +154,7 @@ case object TypeNames extends IRPass {
new MetadataPair(
TypeSignatures,
TypeSignatures.Signature(
resolveSignature(typeParams, bindingsMap, s.signature),
resolveSignature(selfTypeInfo, bindingsMap, s.signature),
s.comment
)
)
@ -121,14 +164,14 @@ case object TypeNames extends IRPass {
}
private def resolveSignature(
typeParams: List[Name],
selfTypeInfo: SelfTypeInfo,
bindingsMap: BindingsMap,
expression: Expression
): Expression =
expression.transformExpressions {
case expr if SuspendedArguments.representsSuspended(expr) => expr
case n: Name.Literal =>
if (typeParams.exists(_.name == n.name)) {
if (selfTypeInfo.typeParams.exists(_.name == n.name)) {
n
} else {
processResolvedName(n, bindingsMap.resolveName(n.name))
@ -138,8 +181,13 @@ case object TypeNames extends IRPass {
n,
bindingsMap.resolveQualifiedName(n.parts.map(_.name))
)
case selfRef: Name.SelfType =>
val resolvedSelfType = selfTypeInfo.selfType.toRight {
BindingsMap.SelfTypeOutsideOfTypeDefinition
}
processResolvedName(selfRef, resolvedSelfType)
case s: `type`.Set =>
s.mapExpressions(resolveSignature(typeParams, bindingsMap, _))
s.mapExpressions(resolveSignature(selfTypeInfo, bindingsMap, _))
}
private def processResolvedName(

View File

@ -12,7 +12,6 @@ import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
public class BinaryDispatchTest extends TestBase {
@ -212,7 +211,6 @@ public class BinaryDispatchTest extends TestBase {
}
@Test
@Ignore // PENDING: #8805
public void staticWithRFirstArgumentIsConverted() {
var rOperator = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "R.---");
@ -251,7 +249,6 @@ public class BinaryDispatchTest extends TestBase {
}
@Test
@Ignore // PENDING #8805
public void staticWithRFirstAndZSecondNoConversionHappens() {
var zOperator = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "Z.---");

View File

@ -398,6 +398,45 @@ public class SignatureTest extends TestBase {
}
}
@Test
public void runtimeCheckOfSelfType() throws Exception {
final URI uri = new URI("memory://self_type_check.enso");
final Source src =
Source.newBuilder(
"enso",
"""
from Standard.Base import all
type My_Type
Value x
f self y = self.x+y
type Other_Type
Ctor x
normal_call = (My_Type.Value 42).f 10
static_call = My_Type.f (My_Type.Value 23) 100
invalid_static_call = My_Type.f (Other_Type.Ctor 11) 1000
""",
uri.getAuthority())
.uri(uri)
.buildLiteral();
var module = ctx.eval(src);
var normal_call = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "normal_call");
assertEquals("Normal call", 52, normal_call.asInt());
var static_call = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "static_call");
assertEquals("Static call", 123, static_call.asInt());
try {
var invalid_static_call =
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "invalid_static_call");
fail("Expecting an exception, not: " + invalid_static_call);
} catch (PolyglotException e) {
assertTypeError("`self`", "My_Type", "Other_Type", e.getMessage());
}
}
@Test
public void wrongAscribedInConstructor() throws Exception {
final URI uri = new URI("memory://constructor.enso");
@ -573,6 +612,55 @@ public class SignatureTest extends TestBase {
assertEquals(42, fourtyTwoAsV.getMember("a").asInt());
}
@Test
public void selfTypeConversion() throws Exception {
final URI uri = new URI("memory://self_type_conversion.enso");
final Source src =
Source.newBuilder(
"enso",
"""
from Standard.Base import all
type My_Type
Value x
f self y = self.x+y
type Convertible_Type
A x
type Inconvertible_Type
B x
My_Type.from (that : Convertible_Type) = My_Type.Value that.x+1
static_my_type = My_Type.f (My_Type.Value 23) 1000
static_convertible = My_Type.f (Convertible_Type.A 23) 1000
static_inconvertible = My_Type.f (Inconvertible_Type.B 23) 1000
""",
uri.getAuthority())
.uri(uri)
.buildLiteral();
var module = ctx.eval(src);
var static_my_type = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "static_my_type");
assertEquals(
"My_Type.f is executed directly on 23, yielding 1023", 1023, static_my_type.asInt());
var convertible = module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "static_convertible");
assertEquals(
"My_Type.f is executed on the converted value, so 23 is incremented to 24, yielding 1024"
+ " proving that the conversion has been applied",
1024,
convertible.asInt());
try {
var invalid_static_call =
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, "static_inconvertible");
fail("Expecting an exception, not: " + invalid_static_call);
} catch (PolyglotException e) {
assertTypeError("`self`", "My_Type", "Inconvertible_Type", e.getMessage());
}
}
private Value exampleWithBinary() throws URISyntaxException {
var uri = new URI("memory://binary.enso");
var src =

View File

@ -208,5 +208,25 @@ class DataflowErrorsTest extends InterpreterTest {
"(Syntax_Error.Error 'Unexpected expression')"
)
}
"report correct stack trace for errors thrown from a suspended default argument" in {
val code =
"""import Standard.Base.IO
|import Standard.Base.Error.Error
|
|type My_Error
| Value
|
|fn x ~y=(Error.throw My_Error.Value) =
| if x == 0 then 0 else y
|
|main =
| IO.println (fn 1).get_stack_trace_text
|""".stripMargin
eval(code)
consumeOut should contain(
" at <enso> <default::Test.fn::y>(Test:7:10-35)"
)
}
}
}

View File

@ -14,7 +14,7 @@ class MethodsTest extends InterpreterTest {
): Unit = {
"be defined in the global scope and dispatched to" in {
val code =
"""
"""import Standard.Base.Data.Numbers
|type Foo
|Foo.bar = number -> number + 1
|main = Foo.bar 10
@ -37,7 +37,7 @@ class MethodsTest extends InterpreterTest {
"be callable with dot operator" in {
val code =
"""
"""import Standard.Base.Data.Numbers
|type Foo
|Foo.bar = number -> number + 1
|main = Foo.bar 10
@ -47,7 +47,7 @@ class MethodsTest extends InterpreterTest {
"be chainable with dot operator" in {
val code =
"""
"""import Standard.Base.Data.Numbers
|type Foo
|type Bar
|type Baz
@ -63,7 +63,7 @@ class MethodsTest extends InterpreterTest {
"behave like parenthesised when called with non-spaced dot operator" in {
val code =
"""
"""import Standard.Base.Data.Numbers
|type Foo
|type Bar
|
@ -78,7 +78,8 @@ class MethodsTest extends InterpreterTest {
"be able to be defined without arguments" in {
val code =
"""
"""import Standard.Base.Data.Numbers
|
|type Foo
|Foo.bar = 1
|main = Foo.bar + 5
@ -89,6 +90,7 @@ class MethodsTest extends InterpreterTest {
"be definable as blocks without arguments" in {
val code =
"""import Standard.Base.Any.Any
|import Standard.Base.Data.Numbers
|
|Any.method self =
| x = self * self
@ -169,7 +171,8 @@ class MethodsTest extends InterpreterTest {
"be callable on types when non-static, with additional self arg" in {
val code =
"""import Standard.Base.IO
"""import Standard.Base.Data.Numbers
|import Standard.Base.IO
|
|type Foo
| Mk_Foo a

View File

@ -11,7 +11,8 @@ class SuspendedArgumentsTest extends InterpreterTest {
"work in basic expressions" in {
val code =
"""
"""import Standard.Base.Data.Numbers
|
|main =
| lazyId : Suspended -> a
| lazyId = x -> x
@ -38,6 +39,7 @@ class SuspendedArgumentsTest extends InterpreterTest {
val code =
"""
|import Standard.Base.Any.Any
|import Standard.Base.Data.Numbers
|
|main =
| ifTest = c -> ~ifT -> ~ifF -> if c == 0 then ifT else ifF
@ -49,7 +51,9 @@ class SuspendedArgumentsTest extends InterpreterTest {
"work in non-tail positions" in {
val code =
"""
"""import Standard.Base.Data.Numbers
|import Standard.Base.Data.Numbers.Number
|
|main =
| suspInc : Suspended -> Number
| suspInc = ~x -> 1 + x

View File

@ -16,7 +16,7 @@ import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(
type = "Vector",
type = "Array_Like_Helpers",
name = "flatten",
description = "Flattens a vector of vectors into a single vector.",
autoRegister = false)
@ -25,15 +25,15 @@ public abstract class FlattenVectorNode extends Node {
return FlattenVectorNodeGen.create();
}
abstract EnsoObject execute(Object self);
abstract EnsoObject execute(Object vector);
@Specialization
EnsoObject flattenAnything(
Object self,
Object vector,
@Cached ArrayLikeCopyToArrayNode copyNode,
@Cached ArrayLikeLengthNode lengthNode,
@Cached ArrayLikeAtNode atNode) {
return flatten(self, copyNode, lengthNode, atNode);
return flatten(vector, copyNode, lengthNode, atNode);
}
private EnsoObject flatten(

View File

@ -10,7 +10,7 @@ import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
@BuiltinMethod(
type = "Vector",
type = "Array_Like_Helpers",
name = "insert_builtin",
description = "Inserts a set of values into the Vector at the specified index.",
autoRegister = false)

View File

@ -10,7 +10,7 @@ import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
@BuiltinMethod(
type = "Vector",
type = "Array_Like_Helpers",
name = "remove_builtin",
description = "Removes a value for the vector at the specified index.",
autoRegister = false)

View File

@ -5,7 +5,10 @@ import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
@BuiltinMethod(type = "Vector", name = "slice", description = "Returns a slice of this Vector.")
@BuiltinMethod(
type = "Array_Like_Helpers",
name = "slice",
description = "Returns a slice of this Vector.")
public final class SliceArrayVectorNode extends Node {
private @Child ArrayLikeLengthNode lengthNode = ArrayLikeLengthNode.create();
@ -15,8 +18,8 @@ public final class SliceArrayVectorNode extends Node {
return new SliceArrayVectorNode();
}
Object execute(Object self, long start, long end) {
var len = lengthNode.executeLength(self);
return ArrayLikeHelpers.slice(self, start, end, len);
Object execute(Object vector, long start, long end) {
var len = lengthNode.executeLength(vector);
return ArrayLikeHelpers.slice(vector, start, end, len);
}
}

View File

@ -2256,7 +2256,7 @@ class IrToTruffle(
*
* @param inputArg the argument to generate code for
* @param position the position of `arg` at the function definition site
* @param checkNode null or node to check the argument type for
* @param types null or node to check the argument type for
* @return a truffle entity corresponding to the definition of `arg` for a
* given function
*/
@ -2273,13 +2273,14 @@ class IrToTruffle(
// Note [Handling Suspended Defaults]
val defaultedValue = if (arg.suspended && defaultExpression != null) {
assert(arg.defaultValue.isDefined)
val defaultRootNode = ClosureRootNode.build(
language,
scope,
moduleScope,
defaultExpression,
null,
s"<default::$scopeName::${arg.name}>",
makeSection(moduleScope, arg.defaultValue.get.location()),
s"<default::$scopeName::${arg.name.showCode()}>",
false,
false
)

View File

@ -539,7 +539,7 @@ type_spec suite_builder name alter = suite_builder.group name group_builder->
vec.slice 1 3 . should_equal [2, 3]
vec.slice 1 1 . should_equal []
vec.slice 0 100 . should_equal [1, 2, 3, 4, 5, 6]
Meta.is_same_object vec (vec.slice 0 100) . should_be_true
Meta.get_qualified_type_name (vec.slice 1 1) . should_equal (Meta.meta Vector . qualified_name)
Meta.get_simple_type_name (vec.slice 1 1) . should_equal "Vector"
@ -1086,4 +1086,3 @@ main =
suite = Test.build suite_builder->
add_specs suite_builder
suite.run_with_filter

View File

@ -5,7 +5,9 @@ import project.Semantic.Definitions.Names
from Standard.Test import all
## This test is important as it also ensures that we can define extension
methods on modules that are not directly imported, but accessed through
a (not-fully) qualified name.
Names.Foo.my_method self = case self of
Names.Foo.Value x y z -> x * y * z

View File

@ -266,7 +266,7 @@ add_specs suite_builder = suite_builder.group "Dataflow Warnings" group_builder-
no_error = warned.remove_warnings Unimplemented
Warning.get_all no_error . map .value . should_equal [1, "Alpha", Nothing]
group_builder.specify "should allow to throwing warnings, by type" <|
group_builder.specify "should allow raising warnings as errors, by type" <|
warned = Warning.attach 1 <| Warning.attach "Alpha" <| Warning.attach Nothing <| Warning.attach (Unimplemented.Error "An Error Here") "foo"
warned.throw_on_warning . should_fail_with Integer

View File

@ -1,6 +1,6 @@
import project.Data.Vector.Vector
from project.Data.Vector import array_like_at, array_like_length
@Builtin_Type
type Array
at self index = Vector.at self index
length self = Vector.length self
at self index = array_like_at self index
length self = array_like_length self