diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index 4a85d3b52..f88f344f5 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Python] F# `task { }` expressions now generate Python `async def` functions (by @dbrattli) +### Fixed + +* [Python] Fix `ResizeArray` compatibility with `Seq`/`Array` (by @dbrattli) + ## 5.0.0-alpha.20 - 2025-12-08 ### Added diff --git a/src/Fable.Compiler/CHANGELOG.md b/src/Fable.Compiler/CHANGELOG.md index 8137f5c18..8d1e7e16d 100644 --- a/src/Fable.Compiler/CHANGELOG.md +++ b/src/Fable.Compiler/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [Python] F# `task { }` expressions now generate Python `async def` functions (by @dbrattli) +### Fixed + +* [Python] Fix `ResizeArray` compatibility with `Seq`/`Array` (by @dbrattli) + ## 5.0.0-alpha.19 - 2025-12-08 ### Added diff --git a/src/fable-library-py/fable_library/core/array.pyi b/src/fable-library-py/fable_library/core/array.pyi index 33bca4382..1f872308e 100644 --- a/src/fable-library-py/fable_library/core/array.pyi +++ b/src/fable-library-py/fable_library/core/array.pyi @@ -196,28 +196,29 @@ class FSharpArray[T](MutableSequence[T]): def zip[U](self, array2: FSharpArray[U]) -> FSharpArray[tuple[T, U]]: ... # Loose functions (alphabetically sorted) +# Note: Many functions accept FSharpArray[T] | Iterable[T] to support ResizeArray (Python list) compatibility def add_in_place[T](array: FSharpArray[T], value: T) -> None: ... def add_range_in_place[T](array: FSharpArray[T], values: Iterable[T]) -> None: ... def allocate_array_from_cons[T](cons: FSharpCons[T] | None, length: SupportsInt) -> FSharpArray[T]: ... -def append[T](array1: FSharpArray[T], array2: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... -def average[T](array: FSharpArray[T], averager: IGenericAverager[T]) -> T: ... -def average_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], averager: IGenericAverager[U]) -> U: ... +def append[T](array1: FSharpArray[T] | Iterable[T], array2: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... +def average[T](array: FSharpArray[T] | Iterable[T], averager: IGenericAverager[T]) -> T: ... +def average_by[T, U](projection: Callable[[T], U], array: FSharpArray[T] | Iterable[T], averager: IGenericAverager[U]) -> U: ... def choose[T, U]( - chooser: Callable[[T], U | None], array: FSharpArray[T], cons: FSharpCons[U] | None = None + chooser: Callable[[T], U | None], array: FSharpArray[T] | Iterable[T], cons: FSharpCons[U] | None = None ) -> FSharpArray[U]: ... -def chunk_by_size[T](chunk_size: SupportsInt, array: FSharpArray[T]) -> FSharpArray[FSharpArray[T]]: ... +def chunk_by_size[T](chunk_size: SupportsInt, array: FSharpArray[T] | Iterable[T]) -> FSharpArray[FSharpArray[T]]: ... def collect[T, U]( - mapping: Callable[[T], FSharpArray[U]], array: FSharpArray[T], cons: FSharpCons[U] | None = None + mapping: Callable[[T], FSharpArray[U]], array: FSharpArray[T] | Iterable[T], cons: FSharpCons[U] | None = None ) -> FSharpArray[U]: ... def compare_to[T]( - comparer: Callable[[T, T], SupportsInt], source1: FSharpArray[T], source2: FSharpArray[T] + comparer: Callable[[T, T], SupportsInt], source1: FSharpArray[T] | Iterable[T], source2: FSharpArray[T] | Iterable[T] ) -> SupportsInt: ... def compare_with[T]( - comparer: Callable[[T, T], SupportsInt], array1: FSharpArray[T], array2: FSharpArray[T] + comparer: Callable[[T, T], SupportsInt], array1: FSharpArray[T] | Iterable[T], array2: FSharpArray[T] | Iterable[T] ) -> SupportsInt: ... def concat[T](arrays: Iterable[FSharpArray[T]], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... -def contains[T](value: T, array: FSharpArray[T], eq: IEqualityComparer[T] | None = None) -> bool: ... -def copy[T](array: FSharpArray[T]) -> FSharpArray[T]: ... +def contains[T](value: T, array: FSharpArray[T] | Iterable[T], eq: IEqualityComparer[T] | None = None) -> bool: ... +def copy[T](array: FSharpArray[T] | Iterable[T]) -> FSharpArray[T]: ... def copy_to[T]( source: FSharpArray[T], source_index: SupportsInt, @@ -227,98 +228,98 @@ def copy_to[T]( ) -> None: ... def create[T](count: SupportsInt, value: T) -> FSharpArray[T]: ... def empty[T](cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... -def equals_with[T](equals_func: Callable[[T, T], bool], array1: FSharpArray[T], array2: FSharpArray[T]) -> bool: ... -def exists[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> bool: ... -def exists_offset[T](predicate: Callable[[T], bool], array: FSharpArray[T], index: int) -> bool: ... +def equals_with[T](equals_func: Callable[[T, T], bool], array1: FSharpArray[T] | Iterable[T], array2: FSharpArray[T] | Iterable[T]) -> bool: ... +def exists[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> bool: ... +def exists_offset[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T], index: int) -> bool: ... def fill[T](array: FSharpArray[T], target_index: int, count: int, value: T) -> FSharpArray[T]: ... -def filter[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> FSharpArray[T]: ... -def find[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> T: ... -def find_back[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> T: ... -def find_index[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> int: ... -def find_index_back[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> int: ... -def find_last_index[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> int: ... -def fold[T, S](folder: Callable[[S, T], S], state: S, array: FSharpArray[T]) -> S: ... -def fold_back[T, S](folder: Callable[[T, S], S], array: FSharpArray[T], state: S) -> S: ... +def filter[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> FSharpArray[T]: ... +def find[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> T: ... +def find_back[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> T: ... +def find_index[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> int: ... +def find_index_back[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> int: ... +def find_last_index[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> int: ... +def fold[T, S](folder: Callable[[S, T], S], state: S, array: FSharpArray[T] | Iterable[T]) -> S: ... +def fold_back[T, S](folder: Callable[[T, S], S], array: FSharpArray[T] | Iterable[T], state: S) -> S: ... def fold_back2[T1, T2, S]( - folder: Callable[[T1, T2, S], S], array1: FSharpArray[T1], array2: FSharpArray[T2], state: S + folder: Callable[[T1, T2, S], S], array1: FSharpArray[T1] | Iterable[T1], array2: FSharpArray[T2] | Iterable[T2], state: S ) -> S: ... -def fold_back_indexed[T, S](folder: Callable[[int, T, S], S], array: FSharpArray[T], state: S) -> S: ... -def fold_back_indexed2[T1, T2, S](folder: Callable[[int, T1, T2, S], S], array2: FSharpArray[T2], state: S) -> S: ... -def fold_indexed[T, S](folder: Callable[[int, Any, Any], Any], state: Any, array: FSharpArray[T]) -> Any: ... -def for_all[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> bool: ... +def fold_back_indexed[T, S](folder: Callable[[int, T, S], S], array: FSharpArray[T] | Iterable[T], state: S) -> S: ... +def fold_back_indexed2[T1, T2, S](folder: Callable[[int, T1, T2, S], S], array1: FSharpArray[T1] | Iterable[T1], array2: FSharpArray[T2] | Iterable[T2], state: S) -> S: ... +def fold_indexed[T, S](folder: Callable[[int, Any, Any], Any], state: Any, array: FSharpArray[T] | Iterable[T]) -> Any: ... +def for_all[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> bool: ... def get_sub_array[T]( - array: FSharpArray[T], start_index: SupportsInt, count: SupportsInt, cons: FSharpCons[T] | None = None + array: FSharpArray[T] | Iterable[T], start_index: SupportsInt, count: SupportsInt, cons: FSharpCons[T] | None = None ) -> FSharpArray[T]: ... -def head[T](array: FSharpArray[T]) -> T: ... +def head[T](array: FSharpArray[T] | Iterable[T]) -> T: ... def index_of[T]( - array: FSharpArray[T], + array: FSharpArray[T] | Iterable[T], item: T, start: SupportsInt | None = None, count: SupportsInt | None = None, eq: IEqualityComparer[T] | None = None, ) -> SupportsInt: ... -def indexed[T](array: FSharpArray[T]) -> FSharpArray[tuple[SupportsInt, T]]: ... +def indexed[T](array: FSharpArray[T] | Iterable[T]) -> FSharpArray[tuple[SupportsInt, T]]: ... def initialize[T]( count: SupportsInt, initializer: Callable[[SupportsInt], T], cons: FSharpCons[T] | None = None ) -> FSharpArray[T]: ... -def insert_at[T](index: SupportsInt, value: T, array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> None: ... +def insert_at[T](index: SupportsInt, value: T, array: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... def insert_many_at[T]( index: SupportsInt, values: FSharpArray[T] | Iterable[T], - array: FSharpArray[T], + array: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None, ) -> FSharpArray[T]: ... def insert_range_in_place[T](array: FSharpArray[T], index: SupportsInt, values: Iterable[T]) -> None: ... -def item[T](index: SupportsInt, array: FSharpArray[T]) -> T: ... -def iterate[T](action: Callable[[T], None], array: FSharpArray[T]) -> None: ... -def iterate_indexed[T](action: Callable[[SupportsInt, T], None], array: FSharpArray[T]) -> None: ... -def last[T](array: FSharpArray[T]) -> T: ... -def map[T, U](f: Callable[[T], U], array: FSharpArray[T], cons: FSharpCons[U] | None = None) -> FSharpArray[U]: ... +def item[T](index: SupportsInt, array: FSharpArray[T] | Iterable[T]) -> T: ... +def iterate[T](action: Callable[[T], None], array: FSharpArray[T] | Iterable[T]) -> None: ... +def iterate_indexed[T](action: Callable[[SupportsInt, T], None], array: FSharpArray[T] | Iterable[T]) -> None: ... +def last[T](array: FSharpArray[T] | Iterable[T]) -> T: ... +def map[T, U](f: Callable[[T], U], array: FSharpArray[T] | Iterable[T], cons: FSharpCons[U] | None = None) -> FSharpArray[U]: ... def map2[T1, T2, U]( - f: Callable[[T1, T2], U], array1: FSharpArray[T1], array2: FSharpArray[T2], cons: FSharpCons[U] | None = None + f: Callable[[T1, T2], U], array1: FSharpArray[T1] | Iterable[T1], array2: FSharpArray[T2] | Iterable[T2], cons: FSharpCons[U] | None = None ) -> FSharpArray[U]: ... def map3[T1, T2, T3, U]( f: Callable[[T1, T2, T3], U], - array1: FSharpArray[T1], - array2: FSharpArray[T2], - array3: FSharpArray[T3], + array1: FSharpArray[T1] | Iterable[T1], + array2: FSharpArray[T2] | Iterable[T2], + array3: FSharpArray[T3] | Iterable[T3], cons: FSharpCons[U] | None = None, ) -> FSharpArray[U]: ... def map_fold[T, S, R]( - mapping: Callable[[S, T], tuple[R, S]], state: S, array: FSharpArray[T], cons: FSharpCons[R] | None = None + mapping: Callable[[S, T], tuple[R, S]], state: S, array: FSharpArray[T] | Iterable[T], cons: FSharpCons[R] | None = None ) -> tuple[FSharpArray[R], S]: ... def map_fold_back[T, S, R]( - mapping: Callable[[T, S], tuple[R, S]], array: FSharpArray[T], state: S, cons: FSharpCons[R] | None = None + mapping: Callable[[T, S], tuple[R, S]], array: FSharpArray[T] | Iterable[T], state: S, cons: FSharpCons[R] | None = None ) -> tuple[FSharpArray[R], S]: ... def map_indexed[T, U]( - f: Callable[[SupportsInt, T], U], array: FSharpArray[T], cons: FSharpCons[U] | None = None + f: Callable[[SupportsInt, T], U], array: FSharpArray[T] | Iterable[T], cons: FSharpCons[U] | None = None ) -> FSharpArray[U]: ... def map_indexed2[T1, T2, U]( f: Callable[[SupportsInt, T1, T2], U], - array1: FSharpArray[T1], - array2: FSharpArray[T2], + array1: FSharpArray[T1] | Iterable[T1], + array2: FSharpArray[T2] | Iterable[T2], cons: FSharpCons[U] | None = None, ) -> FSharpArray[U]: ... def map_indexed3[T1, T2, T3, U]( f: Callable[[SupportsInt, T1, T2, T3], U], - array1: FSharpArray[T1], - array2: FSharpArray[T2], - array3: FSharpArray[T3], + array1: FSharpArray[T1] | Iterable[T1], + array2: FSharpArray[T2] | Iterable[T2], + array3: FSharpArray[T3] | Iterable[T3], cons: FSharpCons[U] | None = None, ) -> FSharpArray[U]: ... -def max[T](array: FSharpArray[T], comparer: IComparer[T]) -> T: ... -def max_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], comparer: IComparer[U]) -> T: ... -def min[T](array: FSharpArray[T], comparer: IComparer[T]) -> T: ... -def min_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], comparer: IComparer[U]) -> T: ... +def max[T](array: FSharpArray[T] | Iterable[T], comparer: IComparer[T]) -> T: ... +def max_by[T, U](projection: Callable[[T], U], array: FSharpArray[T] | Iterable[T], comparer: IComparer[U]) -> T: ... +def min[T](array: FSharpArray[T] | Iterable[T], comparer: IComparer[T]) -> T: ... +def min_by[T, U](projection: Callable[[T], U], array: FSharpArray[T] | Iterable[T], comparer: IComparer[U]) -> T: ... def of_seq[T](seq: Iterable[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... -def pairwise[T](array: FSharpArray[T]) -> FSharpArray[tuple[T, T]]: ... +def pairwise[T](array: FSharpArray[T] | Iterable[T]) -> FSharpArray[tuple[T, T]]: ... def partition[T]( - f: Callable[[T], bool], array: FSharpArray[T], cons: FSharpCons[T] | None = None + f: Callable[[T], bool], array: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None ) -> tuple[FSharpArray[T], FSharpArray[T]]: ... -def permute[T](f: Callable[[SupportsInt], SupportsInt], array: FSharpArray[T]) -> FSharpArray[T]: ... -def pick[T, U](chooser: Callable[[T], U | None], array: FSharpArray[T]) -> U: ... -def reduce[T](reduction: Callable[[T, T], T], array: FSharpArray[T]) -> T: ... -def reduce_back[T](reduction: Callable[[T, T], T], array: FSharpArray[T]) -> T: ... +def permute[T](f: Callable[[SupportsInt], SupportsInt], array: FSharpArray[T] | Iterable[T]) -> FSharpArray[T]: ... +def pick[T, U](chooser: Callable[[T], U | None], array: FSharpArray[T] | Iterable[T]) -> U: ... +def reduce[T](reduction: Callable[[T, T], T], array: FSharpArray[T] | Iterable[T]) -> T: ... +def reduce_back[T](reduction: Callable[[T, T], T], array: FSharpArray[T] | Iterable[T]) -> T: ... def remove_all_in_place[T](array: FSharpArray[T], predicate: Callable[[T], bool]) -> SupportsInt: ... def remove_at[T](index: SupportsInt, array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... def remove_in_place[T](array: FSharpArray[T], item: T) -> bool: ... @@ -326,55 +327,55 @@ def remove_many_at[T](index: SupportsInt, count: SupportsInt, array: FSharpArray def resize[T]( array: FSharpRef[FSharpArray[T]], new_size: SupportsInt, zero: T | None = None, cons: FSharpCons[T] | None = None ) -> FSharpArray[T]: ... -def reverse[T](array: FSharpArray[T]) -> FSharpArray[T]: ... +def reverse[T](array: FSharpArray[T] | Iterable[T]) -> FSharpArray[T]: ... def scan[T, S]( - folder: Callable[[S, T], S], state: S, array: FSharpArray[T], cons: FSharpCons[S] | None = None + folder: Callable[[S, T], S], state: S, array: FSharpArray[T] | Iterable[T], cons: FSharpCons[S] | None = None ) -> FSharpArray[S]: ... def scan_back[T, S]( - folder: Callable[[T, S], S], array: FSharpArray[T], state: S, cons: FSharpCons[S] | None = None + folder: Callable[[T, S], S], array: FSharpArray[T] | Iterable[T], state: S, cons: FSharpCons[S] | None = None ) -> FSharpArray[S]: ... def set_slice[T]( target: FSharpArray[T], lower: SupportsInt | None, upper: SupportsInt | None, array: FSharpArray[T] ) -> None: ... def singleton[T](value: T, cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... -def skip[T](count: SupportsInt, array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... +def skip[T](count: SupportsInt, array: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... def skip_while[T]( - predicate: Callable[[T], bool], array: FSharpArray[T], cons: FSharpCons[T] | None = None + predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None ) -> FSharpArray[T]: ... -def sort[T](array: FSharpArray[T], comparer: IComparer[T]) -> FSharpArray[T]: ... +def sort[T](array: FSharpArray[T] | Iterable[T], comparer: IComparer[T]) -> FSharpArray[T]: ... def sort_by[T]( - projection: Callable[[T], Any], array: FSharpArray[T], comparer: IComparer[T] | None = None + projection: Callable[[T], Any], array: FSharpArray[T] | Iterable[T], comparer: IComparer[T] | None = None ) -> FSharpArray[T]: ... def sort_in_place[T](array: FSharpArray[T], comparer: IComparer[T]) -> None: ... def sort_in_place_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], comparer: IComparer[U]) -> None: ... def sort_in_place_with[T](compare_func: Callable[[T, T], SupportsInt], array: FSharpArray[T]) -> None: ... -def sort_with[T](comparer: Callable[[T, T], SupportsInt], array: FSharpArray[T]) -> FSharpArray[T]: ... -def split_into[T](chunks: SupportsInt, array: FSharpArray[T]) -> FSharpArray[FSharpArray[T]]: ... -def sum[T](array: FSharpArray[T], adder: IGenericAdder[T]) -> T: ... -def sum_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], adder: IGenericAdder[U]) -> U: ... -def tail[T](array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... -def take[T](count: SupportsInt, array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... +def sort_with[T](comparer: Callable[[T, T], SupportsInt], array: FSharpArray[T] | Iterable[T]) -> FSharpArray[T]: ... +def split_into[T](chunks: SupportsInt, array: FSharpArray[T] | Iterable[T]) -> FSharpArray[FSharpArray[T]]: ... +def sum[T](array: FSharpArray[T] | Iterable[T], adder: IGenericAdder[T]) -> T: ... +def sum_by[T, U](projection: Callable[[T], U], array: FSharpArray[T] | Iterable[T], adder: IGenericAdder[U]) -> U: ... +def tail[T](array: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... +def take[T](count: SupportsInt, array: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ... def take_while[T]( - predicate: Callable[[T], bool], array: FSharpArray[T], cons: FSharpCons[T] | None = None + predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None ) -> FSharpArray[T]: ... def transpose[T]( array: FSharpArray[FSharpArray[T]] | Iterable[FSharpArray[T]], cons: FSharpCons[FSharpArray[T]] | None = None ) -> FSharpArray[FSharpArray[T]]: ... -def truncate[T](count: SupportsInt, array: FSharpArray[T]) -> FSharpArray[T]: ... -def try_find[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> T | None: ... -def try_find_back[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> T | None: ... -def try_find_index[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> SupportsInt | None: ... -def try_find_index_back[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> SupportsInt | None: ... -def try_head[T](array: FSharpArray[T]) -> T | None: ... -def try_item[T](index: SupportsInt, array: FSharpArray[T]) -> T | None: ... -def try_last[T](array: FSharpArray[T]) -> T | None: ... -def try_pick[T, U](chooser: Callable[[T], U | None], array: FSharpArray[T]) -> U | None: ... -def unzip[T, U](array: FSharpArray[tuple[T, U]]) -> tuple[FSharpArray[T], FSharpArray[U]]: ... +def truncate[T](count: SupportsInt, array: FSharpArray[T] | Iterable[T]) -> FSharpArray[T]: ... +def try_find[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> T | None: ... +def try_find_back[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> T | None: ... +def try_find_index[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> SupportsInt | None: ... +def try_find_index_back[T](predicate: Callable[[T], bool], array: FSharpArray[T] | Iterable[T]) -> SupportsInt | None: ... +def try_head[T](array: FSharpArray[T] | Iterable[T]) -> T | None: ... +def try_item[T](index: SupportsInt, array: FSharpArray[T] | Iterable[T]) -> T | None: ... +def try_last[T](array: FSharpArray[T] | Iterable[T]) -> T | None: ... +def try_pick[T, U](chooser: Callable[[T], U | None], array: FSharpArray[T] | Iterable[T]) -> U | None: ... +def unzip[T, U](array: FSharpArray[tuple[T, U]] | Iterable[tuple[T, U]]) -> tuple[FSharpArray[T], FSharpArray[U]]: ... def update_at[T]( - index: SupportsInt, value: T, array: FSharpArray[T], cons: FSharpCons[T] | None = None + index: SupportsInt, value: T, array: FSharpArray[T] | Iterable[T], cons: FSharpCons[T] | None = None ) -> FSharpArray[T]: ... -def windowed[T](window_size: SupportsInt, array: FSharpArray[T]) -> FSharpArray[FSharpArray[T]]: ... -def zip[T, U](array1: FSharpArray[T], array2: FSharpArray[U]) -> FSharpArray[tuple[T, U]]: ... +def windowed[T](window_size: SupportsInt, array: FSharpArray[T] | Iterable[T]) -> FSharpArray[FSharpArray[T]]: ... +def zip[T, U](array1: FSharpArray[T] | Iterable[T], array2: FSharpArray[U] | Iterable[U]) -> FSharpArray[tuple[T, U]]: ... class FSharpCons[T]: array_type: ClassVar[str] diff --git a/src/fable-library-py/fable_library/core/floats.pyi b/src/fable-library-py/fable_library/core/floats.pyi index 2a60225f2..8d532e93b 100644 --- a/src/fable-library-py/fable_library/core/floats.pyi +++ b/src/fable-library-py/fable_library/core/floats.pyi @@ -1,8 +1,6 @@ from __future__ import annotations -from typing import Any, ClassVar, Protocol, SupportsFloat, SupportsInt, TypeVar, final - -from typing_extensions import Self +from typing import Any, ClassVar, Protocol, Self, SupportsFloat, SupportsInt, TypeVar, final # Define a generic type variable for the protocol _FT = TypeVar("_FT", bound="FloatNumeric") diff --git a/src/fable-library-py/src/array.rs b/src/fable-library-py/src/array.rs index e4bfc7e47..b5ff4d285 100644 --- a/src/fable-library-py/src/array.rs +++ b/src/fable-library-py/src/array.rs @@ -73,27 +73,81 @@ macro_rules! try_extract_array { }; } -// Utility function to convert Python objects to FSharpArray. -fn ensure_array(py: Python<'_>, ob: &Bound<'_, PyAny>) -> PyResult { - // If it's already a FSharpArray, just extract it - if let Ok(array) = ob.extract::>() { - return Ok(array.clone()); +/// A cow-like wrapper that either borrows from an existing Python FSharpArray or owns a new one. +/// +/// # Performance Optimization +/// +/// This type avoids expensive deep clones when converting Python objects to FSharpArray. +/// When the input is already an FSharpArray, we borrow it directly (zero-copy). +/// When the input is something else (list, iterable, etc.), we create a new owned array. +/// +/// The `Deref` implementation allows transparent access to `&FSharpArray` methods. +/// Use `into_owned()` only when mutation or ownership transfer is required. +enum ArrayRef<'py> { + /// Borrowed reference to an existing Python FSharpArray (no clone) + Borrowed(PyRef<'py, FSharpArray>), + /// Newly created array that we own + Owned(FSharpArray), +} + +impl std::ops::Deref for ArrayRef<'_> { + type Target = FSharpArray; + + fn deref(&self) -> &Self::Target { + match self { + ArrayRef::Borrowed(r) => r, + ArrayRef::Owned(a) => a, + } + } +} + +impl ArrayRef<'_> { + /// Convert to an owned FSharpArray. + /// + /// - If borrowed: clones the array data (unavoidable when ownership is needed) + /// - If owned: moves without copying + /// + /// Use this only when you need ownership, e.g., for mutation or storing in collections. + fn into_owned(self) -> FSharpArray { + match self { + ArrayRef::Borrowed(r) => r.clone(), + ArrayRef::Owned(a) => a, + } + } +} + +/// Converts a Python object to an FSharpArray reference. +/// +/// # Performance Optimization +/// +/// This function returns `ArrayRef` instead of `FSharpArray` to avoid cloning +/// when the input is already an FSharpArray. This is a common case in F# code +/// where arrays are passed through multiple function calls. +/// +/// - If `ob` is an FSharpArray: returns a borrowed reference (O(1), no allocation) +/// - If `ob` is None: returns an empty array +/// - If `ob` is iterable: converts to a new array +/// - Otherwise: wraps as a singleton array +fn ensure_array<'py>(py: Python<'py>, ob: &'py Bound<'py, PyAny>) -> PyResult> { + // If it's already a FSharpArray, borrow it (no clone) + if let Ok(array) = ob.cast::() { + return Ok(ArrayRef::Borrowed(array.borrow())); } // If the object is None (null), create an empty array if ob.is_none() { - return FSharpArray::new(py, None, None); + return Ok(ArrayRef::Owned(FSharpArray::new(py, None, None)?)); } // Check if the object is iterable if let Ok(iter) = ob.try_iter() { // Convert iterable directly to FSharpArray - return FSharpArray::new(py, Some(iter.as_any()), None); + return Ok(ArrayRef::Owned(FSharpArray::new(py, Some(iter.as_any()), None)?)); } // If it's a single item, create a singleton array let singleton_list = PyList::new(py, [ob])?; - FSharpArray::new(py, Some(&singleton_list), None) + Ok(ArrayRef::Owned(FSharpArray::new(py, Some(&singleton_list), None)?)) } #[pymethods] @@ -390,11 +444,31 @@ impl FSharpArray { self.storage.len() } - pub fn __iter__(&self, py: Python<'_>) -> PyResult> { + /// Returns an iterator over the array elements. + /// + /// # Performance Optimization + /// + /// This method takes `PyRef<'_, Self>` instead of `&self` to access the underlying + /// Python object reference. This allows us to increment the reference count via + /// `Py::from_borrowed_ptr` instead of cloning the entire array data. + /// + /// The iterator holds a `Py` which keeps the array alive during iteration. + /// This is O(1) (just a refcount increment) vs O(n) for cloning the array contents. + /// + /// # Safety + /// + /// The `unsafe` block is safe because: + /// - `slf.as_ptr()` returns a valid pointer to the Python object + /// - `from_borrowed_ptr` increments the refcount, ensuring the object stays alive + /// - The `PyRef` guarantees we have a valid borrow of the object + pub fn __iter__(slf: PyRef<'_, Self>, py: Python<'_>) -> PyResult> { + let len = slf.storage.len(); + // SAFETY: slf.as_ptr() is valid and from_borrowed_ptr increments refcount + let array: Py = unsafe { Py::from_borrowed_ptr(py, slf.as_ptr()) }; let iter = FSharpArrayIter { - array: Py::new(py, self.clone())?, + array, index: 0, - len: self.storage.len(), + len, }; iter.into_py_any(py) } @@ -428,10 +502,8 @@ impl FSharpArray { let serializer_fn = cls.getattr("_pydantic_serializer")?; // Build the serialization schema - let ser_schema = core_schema.call_method1( - "plain_serializer_function_ser_schema", - (serializer_fn,), - )?; + let ser_schema = + core_schema.call_method1("plain_serializer_function_ser_schema", (serializer_fn,))?; // Create a list schema as the base - this enables JSON Schema generation let list_schema = core_schema.call_method0("list_schema")?; @@ -3125,13 +3197,13 @@ impl FSharpArray { let len = self.storage.len(); // First, map each element to an array - let mut mapped_arrays = Vec::with_capacity(len); + let mut mapped_arrays: Vec = Vec::with_capacity(len); for i in 0..len { let item = self.get_item_at_index(i as isize, py)?; let mapped_result = mapping.call1((item,))?; - // Convert the result to a FSharpArray - let mapped_array = ensure_array(py, &mapped_result)?; + // Convert the result to a FSharpArray (need owned for collection) + let mapped_array = ensure_array(py, &mapped_result)?.into_owned(); mapped_arrays.push(mapped_array); } @@ -3164,10 +3236,11 @@ impl FSharpArray { #[pyo3(signature = (array1, array2, cons=None))] pub fn append( py: Python<'_>, - array1: &FSharpArray, + array1: &Bound<'_, PyAny>, array2: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array1 = ensure_array(py, array1)?; array1.append(py, array2, cons) } @@ -3197,30 +3270,35 @@ pub fn create(py: Python<'_>, count: usize, value: &Bound<'_, PyAny>) -> PyResul pub fn map( py: Python<'_>, f: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.map(py, f, cons) } #[pyfunction] +#[pyo3(signature = (f, array1, array2, cons=None))] pub fn map2( py: Python<'_>, f: &Bound<'_, PyAny>, - array1: &FSharpArray, + array1: &Bound<'_, PyAny>, array2: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array1 = ensure_array(py, array1)?; array1.map2(py, f, array2, cons) } #[pyfunction] +#[pyo3(signature = (f, array, cons=None))] pub fn map_indexed( py: Python<'_>, f: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.map_indexed(py, f, cons) } @@ -3228,8 +3306,9 @@ pub fn map_indexed( pub fn filter( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult { + let array = ensure_array(py, array)?; array.filter(py, predicate) } @@ -3238,9 +3317,10 @@ pub fn filter( pub fn skip( py: Python<'_>, count: isize, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.skip(py, count, cons) } @@ -3249,9 +3329,10 @@ pub fn skip( pub fn skip_while( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.skip_while(py, predicate, cons) } @@ -3260,9 +3341,10 @@ pub fn skip_while( pub fn take_while( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.take_while(py, predicate, cons) } @@ -3270,8 +3352,9 @@ pub fn take_while( pub fn chunk_by_size( py: Python<'_>, chunk_size: usize, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult { + let array = ensure_array(py, array)?; array.chunk_by_size(py, chunk_size) } @@ -3291,8 +3374,9 @@ pub fn fold( py: Python<'_>, folder: &Bound<'_, PyAny>, state: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.fold(py, folder, state) } @@ -3301,8 +3385,9 @@ pub fn fold_indexed( py: Python<'_>, folder: &Bound<'_, PyAny>, state: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.fold_indexed(py, folder, state) } @@ -3310,9 +3395,10 @@ pub fn fold_indexed( pub fn fold_back( py: Python<'_>, folder: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, state: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.fold_back(py, folder, state) } @@ -3320,9 +3406,10 @@ pub fn fold_back( pub fn fold_back_indexed( py: Python<'_>, folder: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, state: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.fold_back_indexed(py, folder, state) } @@ -3358,9 +3445,10 @@ pub fn sort_in_place_by( pub fn equals_with( py: Python<'_>, equals_func: &Bound<'_, PyAny>, - array1: &FSharpArray, + array1: &Bound<'_, PyAny>, array2: &Bound<'_, PyAny>, ) -> PyResult { + let array1 = ensure_array(py, array1)?; array1.equals_with(py, equals_func, array2) } @@ -3379,8 +3467,8 @@ pub fn resize( let current_array_obj = fs_ref.get_contents(py)?; let current_array_bound = current_array_obj.bind(py); - // Convert to FSharpArray (ensure_array now handles None as empty array) - let mut current_array = ensure_array(py, current_array_bound)?; + // Convert to FSharpArray (need owned for mutation) + let mut current_array = ensure_array(py, current_array_bound)?.into_owned(); // Resize the array current_array.resize(py, new_size, zero, cons)?; @@ -3411,8 +3499,9 @@ pub fn reduce( pub fn reduce_back( py: Python<'_>, reduction: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.reduce_back(py, reduction) } @@ -3420,10 +3509,11 @@ pub fn reduce_back( pub fn fold_back_indexed2( py: Python<'_>, folder: &Bound<'_, PyAny>, - array1: &FSharpArray, + array1: &Bound<'_, PyAny>, array2: &Bound<'_, PyAny>, state: &Bound<'_, PyAny>, ) -> PyResult> { + let array1 = ensure_array(py, array1)?; array1.fold_back_indexed2(py, folder, array2, state) } @@ -3431,15 +3521,17 @@ pub fn fold_back_indexed2( pub fn fold_back2( py: Python<'_>, f: &Bound<'_, PyAny>, - array1: &FSharpArray, + array1: &Bound<'_, PyAny>, array2: &Bound<'_, PyAny>, state: &Bound<'_, PyAny>, ) -> PyResult> { + let array1 = ensure_array(py, array1)?; array1.fold_back2(py, f, array2, state) } #[pyfunction] -pub fn iterate(py: Python<'_>, action: &Bound<'_, PyAny>, array: &FSharpArray) -> PyResult<()> { +pub fn iterate(py: Python<'_>, action: &Bound<'_, PyAny>, array: &Bound<'_, PyAny>) -> PyResult<()> { + let array = ensure_array(py, array)?; array.iterate(py, action) } @@ -3447,8 +3539,9 @@ pub fn iterate(py: Python<'_>, action: &Bound<'_, PyAny>, array: &FSharpArray) - pub fn iterate_indexed( py: Python<'_>, action: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult<()> { + let array = ensure_array(py, array)?; array.iterate_indexed(py, action) } @@ -3490,7 +3583,8 @@ pub fn average_by( } #[pyfunction] -pub fn pairwise(py: Python<'_>, array: &FSharpArray) -> PyResult { +pub fn pairwise(py: Python<'_>, array: &Bound<'_, PyAny>) -> PyResult { + let array = ensure_array(py, array)?; array.pairwise(py) } @@ -3498,8 +3592,9 @@ pub fn pairwise(py: Python<'_>, array: &FSharpArray) -> PyResult { pub fn permute( py: Python<'_>, f: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.permute(py, f) } @@ -3509,9 +3604,10 @@ pub fn scan( py: Python<'_>, folder: &Bound<'_, PyAny>, state: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.scan(py, folder, state, cons) } @@ -3520,15 +3616,17 @@ pub fn scan( pub fn scan_back( py: Python<'_>, folder: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, state: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.scan_back(py, folder, state, cons) } #[pyfunction] -pub fn split_into(py: Python<'_>, chunks: usize, array: &FSharpArray) -> PyResult { +pub fn split_into(py: Python<'_>, chunks: usize, array: &Bound<'_, PyAny>) -> PyResult { + let array = ensure_array(py, array)?; array.split_into(py, chunks) } @@ -3547,8 +3645,9 @@ pub fn transpose( pub fn try_find_back( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult>> { + let array = ensure_array(py, array)?; array.try_find_back(py, predicate) } @@ -3556,13 +3655,15 @@ pub fn try_find_back( pub fn try_find_index_back( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.try_find_index_back(py, predicate) } #[pyfunction] -pub fn windowed(py: Python<'_>, window_size: usize, array: &FSharpArray) -> PyResult { +pub fn windowed(py: Python<'_>, window_size: usize, array: &Bound<'_, PyAny>) -> PyResult { + let array = ensure_array(py, array)?; array.windowed(py, window_size) } @@ -3572,9 +3673,10 @@ pub fn map_fold( py: Python<'_>, mapping: &Bound<'_, PyAny>, state: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.map_fold(py, mapping, state, cons) } @@ -3592,12 +3694,14 @@ pub fn map_fold_back( } #[pyfunction] -pub fn head(py: Python<'_>, array: &FSharpArray) -> PyResult> { +pub fn head(py: Python<'_>, array: &Bound<'_, PyAny>) -> PyResult> { + let array = ensure_array(py, array)?; array.head(py) } #[pyfunction] -pub fn try_head(py: Python<'_>, array: &FSharpArray) -> PyResult>> { +pub fn try_head(py: Python<'_>, array: &Bound<'_, PyAny>) -> PyResult>> { + let array = ensure_array(py, array)?; array.try_head(py) } @@ -3605,24 +3709,32 @@ pub fn try_head(py: Python<'_>, array: &FSharpArray) -> PyResult, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.tail(py, cons) } #[pyfunction] -pub fn item(py: Python<'_>, index: isize, array: &FSharpArray) -> PyResult> { +pub fn item(py: Python<'_>, index: isize, array: &Bound<'_, PyAny>) -> PyResult> { + let array = ensure_array(py, array)?; array.item(py, index) } #[pyfunction] -pub fn try_item(py: Python<'_>, index: isize, array: &FSharpArray) -> PyResult>> { +pub fn try_item( + py: Python<'_>, + index: isize, + array: &Bound<'_, PyAny>, +) -> PyResult>> { + let array = ensure_array(py, array)?; array.try_item(py, index) } #[pyfunction] -pub fn reverse(py: Python<'_>, array: &FSharpArray) -> PyResult { +pub fn reverse(py: Python<'_>, array: &Bound<'_, PyAny>) -> PyResult { + let array = ensure_array(py, array)?; array.reverse(py) } @@ -3641,24 +3753,28 @@ pub fn initialize( pub fn compare_with( py: Python<'_>, comparer: &Bound<'_, PyAny>, - array1: &FSharpArray, - array2: &FSharpArray, + array1: &Bound<'_, PyAny>, + array2: &Bound<'_, PyAny>, ) -> PyResult { - array1.compare_with(py, comparer, array2) + let array1 = ensure_array(py, array1)?; + let array2 = ensure_array(py, array2)?; + array1.compare_with(py, comparer, &array2) } #[pyfunction] pub fn exists_offset( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, index: usize, ) -> PyResult { + let array = ensure_array(py, array)?; array.exists_offset(py, predicate, index) } #[pyfunction] -pub fn exists(py: Python<'_>, predicate: &Bound<'_, PyAny>, array: &FSharpArray) -> PyResult { +pub fn exists(py: Python<'_>, predicate: &Bound<'_, PyAny>, array: &Bound<'_, PyAny>) -> PyResult { + let array = ensure_array(py, array)?; array.exists(py, predicate) } @@ -3668,9 +3784,10 @@ pub fn update_at( py: Python<'_>, index: usize, value: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.update_at(py, index, value, cons) } @@ -3686,25 +3803,29 @@ pub fn set_slice( } #[pyfunction] +#[pyo3(signature = (index, value, array, cons=None))] pub fn insert_at( py: Python<'_>, index: usize, value: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.insert_at(py, index, value, cons) } #[pyfunction] +#[pyo3(signature = (index, values, array, cons=None))] pub fn insert_many_at( py: Python<'_>, index: usize, - ys: &Bound<'_, PyAny>, - array: &FSharpArray, + values: &Bound<'_, PyAny>, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { - array.insert_many_at(py, index, ys, cons) + let array = ensure_array(py, array)?; + array.insert_many_at(py, index, values, cons) } #[pyfunction] @@ -3712,12 +3833,15 @@ pub fn insert_many_at( pub fn map3( py: Python<'_>, f: &Bound<'_, PyAny>, - array1: &FSharpArray, - array2: &FSharpArray, - array3: &FSharpArray, + array1: &Bound<'_, PyAny>, + array2: &Bound<'_, PyAny>, + array3: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { - array1.map3(py, f, array2, array3, cons) + let array1 = ensure_array(py, array1)?; + let array2 = ensure_array(py, array2)?; + let array3 = ensure_array(py, array3)?; + array1.map3(py, f, &array2, &array3, cons) } #[pyfunction] @@ -3725,11 +3849,13 @@ pub fn map3( pub fn map_indexed2( py: Python<'_>, f: &Bound<'_, PyAny>, - array1: &FSharpArray, - array2: &FSharpArray, + array1: &Bound<'_, PyAny>, + array2: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { - array1.map_indexed2(py, f, array2, cons) + let array1 = ensure_array(py, array1)?; + let array2 = ensure_array(py, array2)?; + array1.map_indexed2(py, f, &array2, cons) } #[pyfunction] @@ -3737,12 +3863,15 @@ pub fn map_indexed2( pub fn map_indexed3( py: Python<'_>, f: &Bound<'_, PyAny>, - array1: &FSharpArray, - array2: &FSharpArray, - array3: &FSharpArray, + array1: &Bound<'_, PyAny>, + array2: &Bound<'_, PyAny>, + array3: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { - array1.map_indexed3(py, f, array2, array3, cons) + let array1 = ensure_array(py, array1)?; + let array2 = ensure_array(py, array2)?; + let array3 = ensure_array(py, array3)?; + array1.map_indexed3(py, f, &array2, &array3, cons) } #[pyfunction] @@ -3772,14 +3901,16 @@ pub fn remove_in_place( } #[pyfunction] +#[pyo3(signature = (array, item, start=None, count=None, eq=None))] pub fn index_of( py: Python<'_>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, item: &Bound<'_, PyAny>, start: Option, count: Option, eq: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.index_of(py, item, start, count, eq) } @@ -3798,9 +3929,10 @@ pub fn copy_to( #[pyfunction] pub fn zip( py: Python<'_>, - array1: &FSharpArray, + array1: &Bound<'_, PyAny>, array2: &Bound<'_, PyAny>, ) -> PyResult { + let array1 = ensure_array(py, array1)?; array1.zip(py, array2) } @@ -3808,8 +3940,9 @@ pub fn zip( pub fn for_all( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult { + let array = ensure_array(py, array)?; array.for_all(py, predicate) } @@ -3817,8 +3950,9 @@ pub fn for_all( pub fn find( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.find(py, predicate) } @@ -3826,8 +3960,9 @@ pub fn find( pub fn try_find( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult>> { + let array = ensure_array(py, array)?; array.try_find(py, predicate) } @@ -3835,8 +3970,9 @@ pub fn try_find( pub fn find_last_index( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult { + let array = ensure_array(py, array)?; array.find_last_index(py, predicate) } @@ -3872,11 +4008,12 @@ pub fn insert_range_in_place( #[pyo3(signature = (array, start_index, count, cons=None))] pub fn get_sub_array( py: Python<'_>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, start_index: isize, count: usize, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.get_sub_array(py, start_index, count, cons) } @@ -3886,9 +4023,10 @@ pub fn get_sub_array( pub fn contains( py: Python<'_>, value: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, eq: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.contains(py, value, eq) } @@ -3925,9 +4063,10 @@ pub fn min( pub fn choose( py: Python<'_>, chooser: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.choose(py, chooser, cons) } @@ -3936,9 +4075,10 @@ pub fn choose( pub fn collect( py: Python<'_>, mapping: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.collect(py, mapping, cons) } @@ -3970,8 +4110,9 @@ pub fn min_by( pub fn find_back( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.find_back(py, predicate) } @@ -3979,8 +4120,9 @@ pub fn find_back( pub fn pick( py: Python<'_>, chooser: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.pick(py, chooser) } @@ -3988,8 +4130,9 @@ pub fn pick( pub fn try_pick( py: Python<'_>, chooser: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult>> { + let array = ensure_array(py, array)?; array.try_pick(py, chooser) } @@ -4003,7 +4146,8 @@ pub fn remove_all_in_place( } #[pyfunction] -pub fn indexed(py: Python<'_>, array: &FSharpArray) -> PyResult { +pub fn indexed(py: Python<'_>, array: &Bound<'_, PyAny>) -> PyResult { + let array = ensure_array(py, array)?; array.indexed(py) } @@ -4011,23 +4155,27 @@ pub fn indexed(py: Python<'_>, array: &FSharpArray) -> PyResult { pub fn try_find_index( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult> { + let array = ensure_array(py, array)?; array.try_find_index(py, predicate) } #[pyfunction] -pub fn try_last(py: Python<'_>, array: &FSharpArray) -> PyResult>> { +pub fn try_last(py: Python<'_>, array: &Bound<'_, PyAny>) -> PyResult>> { + let array = ensure_array(py, array)?; array.try_last(py) } #[pyfunction] -pub fn last(py: Python<'_>, array: &FSharpArray) -> PyResult> { +pub fn last(py: Python<'_>, array: &Bound<'_, PyAny>) -> PyResult> { + let array = ensure_array(py, array)?; array.last(py) } #[pyfunction] -pub fn truncate(py: Python<'_>, count: isize, array: &FSharpArray) -> PyResult { +pub fn truncate(py: Python<'_>, count: isize, array: &Bound<'_, PyAny>) -> PyResult { + let array = ensure_array(py, array)?; array.truncate(py, count) } @@ -4036,9 +4184,10 @@ pub fn truncate(py: Python<'_>, count: isize, array: &FSharpArray) -> PyResult, f: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, cons: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.partition(py, f, cons) } @@ -4096,8 +4245,9 @@ pub fn concat( pub fn find_index( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult { + let array = ensure_array(py, array)?; array.find_index(py, predicate) } @@ -4105,17 +4255,19 @@ pub fn find_index( pub fn find_index_back( py: Python<'_>, predicate: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult { + let array = ensure_array(py, array)?; array.find_index_back(py, predicate) } #[pyfunction] pub fn sort( py: Python<'_>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, comparer: &Bound<'_, PyAny>, ) -> PyResult { + let array = ensure_array(py, array)?; array.sort(py, comparer) } @@ -4124,9 +4276,10 @@ pub fn sort( pub fn sort_by( py: Python<'_>, projection: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, comparer: Option<&Bound<'_, PyAny>>, ) -> PyResult { + let array = ensure_array(py, array)?; array.sort_by(py, projection, comparer) } @@ -4134,8 +4287,9 @@ pub fn sort_by( pub fn sort_with( py: Python<'_>, comparer: &Bound<'_, PyAny>, - array: &FSharpArray, + array: &Bound<'_, PyAny>, ) -> PyResult { + let array = ensure_array(py, array)?; array.sort_with(py, comparer) } @@ -4152,7 +4306,8 @@ pub fn sum_by( } #[pyfunction] -pub fn unzip(py: Python<'_>, array: &FSharpArray) -> PyResult> { +pub fn unzip(py: Python<'_>, array: &Bound<'_, PyAny>) -> PyResult> { + let array = ensure_array(py, array)?; array.unzip(py) } @@ -4172,10 +4327,12 @@ pub fn take( pub fn compare_to( py: Python<'_>, comparer: &Bound<'_, PyAny>, - source1: &FSharpArray, - source2: &FSharpArray, + source1: &Bound<'_, PyAny>, + source2: &Bound<'_, PyAny>, ) -> PyResult { - source1.compare_to(py, comparer, source2) + let source1 = ensure_array(py, source1)?; + let source2 = ensure_array(py, source2)?; + source1.compare_to(py, comparer, &source2) } #[pyfunction] @@ -4370,10 +4527,7 @@ impl BoolArray { #[new] #[pyo3(signature = (elements=None))] fn new(py: Python<'_>, elements: Option<&Bound<'_, PyAny>>) -> PyResult<(Self, FSharpArray)> { - Ok(( - BoolArray {}, - FSharpArray::new(py, elements, Some("Bool"))?, - )) + Ok((BoolArray {}, FSharpArray::new(py, elements, Some("Bool"))?)) } } diff --git a/tests/Python/TestResizeArray.fs b/tests/Python/TestResizeArray.fs index 48af7b818..bec4bc71c 100644 --- a/tests/Python/TestResizeArray.fs +++ b/tests/Python/TestResizeArray.fs @@ -337,4 +337,14 @@ let ``test ResizeArray.IndexOf works with non-primitive types`` () = myResizeArray.Add(Dog 3) myResizeArray.IndexOf(Dog 3) |> equal 1 myResizeArray.IndexOf(Dog 3, 0, 1) |> equal -1 - myResizeArray.IndexOf(Duck 5, 1) |> equal -1 \ No newline at end of file + myResizeArray.IndexOf(Duck 5, 1) |> equal -1 + +[] +let ``test ResizeArray works with Seq.item`` () = + let li = ResizeArray([1; 2; 3]) + let firstItem = Seq.item 0 li + equal 1 firstItem + let secondItem = Seq.item 1 li + equal 2 secondItem + let thirdItem = Seq.item 2 li + equal 3 thirdItem \ No newline at end of file