ITADN

版本发布 8

Release 0.20.00.20.0
? · 2026-05-21

# Pyochain v0.20.0 This release follow the pattern of 0.18 and 0.19 regarding changes for a leaner, cleaner API with better semantics. Note that besides the first 4 points in the "breaking changes" section, the rest won't impact you unless you defined custom subclasses from the former `traits` module. ## Changes ### 💥 Breaking changes - **API change**: `pyochain::traits` module is now `pyochain::abc`. If you were importing from `pyochain.traits`, update your imports to `pyochain.abc` instead. - **Removed**: `PyoIterable::new`. Call `__init__(())` for the same behavior, e.g `Seq(())`, `Iter(())`, etc... - **Removed**: `Unzipped` and `Peekable` dataclasses. The `Iter` methods who constructed them now simply return tuples instead, simplifying the API and improving speed. - **API change**: `Iter::sort` (now in base class `PyoIterator`) has been split into `Iter::sort` and `Iter::sort_by`. If you were using `Iter::sort(key=...)`, you should now use `Iter::sort_by(key=...)` instead. This should bring typing improvements as well as a clearer API. - **API change**: `PyoIterable::__init__` is deleted. This means that subclasses are free to implement their own constructors, without typing constraints nor default behavior. #### Methods migration to concrete parents If you did not define custom classes from `PyoSet` or `PyoIterator`, skip to the *Enhancements* section. --- The `__init__` deletion from `PyoIterable` also means that all ABCs methods that relied on `self::__class__` needed to move to their concrete pyochain parents, as they couldn't stay (nor should have ever been) purely abstract. This concerns: from `PyoSet` to `Set` -> `intersection`, `r_intersection`, `union`, `r_union`, `difference`, `r_difference`, `symmetric_difference`, `r_symmetric_difference` --- from `PyoIterator` to `Iter` -> `take_while`, `skip_while`, `compress`, `unique`, `unique_by`, `take`, `skip`, `step_by`, `slice`, `cycle`, `insert`, `intersperse`, `chain`, `accumulate` --- ### ✨ Enhancements - **Migrated**: `Iter::{collect_into, try_collect, sort, tail}` have been moved to `PyoIterator`, meaning all user-defined subclasses can now call them (as well as `sort_by`) - **Migrated**: `Vec::{drain, extract_if}` have been moved to `PyoMutableSequence`, meaning all user-defined mutable sequence subclasses can now call them. ### 📖 Documentation - Improved various classes and methods documentation. - Reformatted all code examples with Ruff. - Added dev documentation regarding design choices on where to implement a method (abstract vs concrete class). ### 🔄 Refactors - Separated some classes in dedicated files to reduce the size of `_iter.py` and improve readability. `Seq` is now in `_seq.py`, `Set` and `SetMut` are now in `_set.py` notably.

Release 0.9.20.9.2
? · 2026-01-16

# Release 0.9.2 ## 🚀 pyochain 0.9.2 — Release Notes **Date:** 2026-01-16 📅 ## 🆕 Highlights This release focuses on performance improvements through Rust migration and minor fixes to typing and keyword argument handling.: - **Rust Migration**: Moved 10 core iterator methods to Rust — Up to **3.09x faster** for comparison operations, **2.39x faster** for sorting checks - **Type Safety Improvements**: Enhanced `Result.flatten()` type inference for better IDE support and fewer false positives - **Code Quality**: Improved docstrings, internal tooling, and API consistency across Rust implementations - **Kwargs bugfix**: Fixed misalignment of keyword arguments in methods like `map_or_else` between `{Ok, Some}` and `{Err, None}` variants --- ## 🔥 Performance Improvements ### Rust Migration: Iterator Comparison & Sorting Methods Migrated 10 `PyIterator` methods from Python to Rust: - **Lazy comparison operators**: `eq`, `ne`, `lt`, `gt`, `le`, `ge` - **Sorting checks**: `is_sorted`, `is_sorted_by` - **Functional operations**: `try_fold`, `try_reduce` This migration provides a median speedup of **+105%** across all methods, with marked improvements on comparisons and sorting operations. #### Details Performance results across 2,500 runs with 10 function calls each: ```shell ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓ ┃ Category ┃ Operation ┃ Runs ┃ New (μs, median) ┃ Old (μs, median) ┃ Speedup ┃ ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩ │ eq_test │ comp_eq │ 2500 │ 349.6 │ 1080.9 │ 3.09x │ │ comparison │ comp_lt │ 2500 │ 368.5 │ 1105.3 │ 3.0x │ │ comparison │ comp_ne │ 2500 │ 366.1 │ 1090.3 │ 2.98x │ │ comparison │ comp_gt │ 2500 │ 368.35 │ 1096.0 │ 2.98x │ │ comparison │ comp_ge │ 2500 │ 368.3 │ 1095.85 │ 2.98x │ │ comparison │ comp_le │ 2500 │ 367.55 │ 1082.5 │ 2.95x │ │ is_sorted │ is_sorted_asc_exit100 │ 2500 │ 146.6 │ 349.7 │ 2.39x │ │ is_sorted │ is_sorted_desc_exit100 │ 2500 │ 151.7 │ 358.5 │ 2.36x │ │ is_sorted │ is_sorted_asc_strict_exit100 │ 2500 │ 150.0 │ 350.2 │ 2.33x │ │ is_sorted │ is_sorted_desc_exit50 │ 2500 │ 81.7 │ 188.6 │ 2.31x │ │ is_sorted │ is_sorted_asc_exit50 │ 2500 │ 78.4 │ 180.2 │ 2.3x │ │ is_sorted │ is_sorted_asc_strict_exit50 │ 2500 │ 79.7 │ 182.25 │ 2.29x │ │ is_sorted │ is_sorted_desc_strict_exit100 │ 2500 │ 150.9 │ 342.9 │ 2.27x │ │ is_sorted │ is_sorted_desc_strict_exit50 │ 2500 │ 82.3 │ 181.5 │ 2.21x │ │ is_sorted_by │ is_sorted_by_desc_exit50 │ 2500 │ 376.7 │ 711.45 │ 1.89x │ │ is_sorted_by │ is_sorted_by_desc_exit100 │ 2500 │ 744.6 │ 1399.75 │ 1.88x │ │ is_sorted_by │ is_sorted_by_asc_strict_exit50 │ 2500 │ 379.6 │ 712.6 │ 1.88x │ │ is_sorted_by │ is_sorted_by_asc_strict_exit100 │ 2500 │ 755.8 │ 1402.0 │ 1.85x │ │ is_sorted_by │ is_sorted_by_asc_exit100 │ 2500 │ 759.9 │ 1368.25 │ 1.8x │ │ is_sorted_by │ is_sorted_by_asc_exit50 │ 2500 │ 387.5 │ 686.4 │ 1.77x │ │ is_sorted_by │ is_sorted_by_desc_strict_exit50 │ 2500 │ 386.0 │ 678.1 │ 1.76x │ │ is_sorted_by │ is_sorted_by_desc_strict_exit100 │ 2500 │ 764.3 │ 1341.2 │ 1.75x │ │ try_reduce │ try_reduce │ 2500 │ 287.8 │ 418.8 │ 1.46x │ │ try_fold │ try_fold │ 2500 │ 293.0 │ 418.7 │ 1.43x │ │ try_fold │ try_fold_conditional_logic │ 2500 │ 643.0 │ 895.8 │ 1.39x │ │ try_reduce │ try_reduce_conditional_logic │ 2500 │ 650.0 │ 888.6 │ 1.37x │ │ try_fold │ try_fold_string_accumulation │ 2500 │ 460.5 │ 595.6 │ 1.29x │ │ try_reduce │ try_reduce_string_accumulation │ 2500 │ 452.8 │ 585.0 │ 1.29x │ └──────────────┴──────────────────────────────────┴──────┴──────────────────┴──────────────────┴─────────┘ Median speedup: 2.05x New wins: 28/28 ``` ---

Release 0.9.10.9.1
? · 2026-01-15

# 🚀 pyochain 0.9.1 — Release Notes Date: 2026-01-15 📅 ## 🆕 Highlights This release is a critical bugfix and performance improvement release: - **CRITICAL FIX**: Fixed a severe performance regression introduced in *0.7.0* where iterating over `Iter` objects fell back to slow `__next__()` calls instead of delegating to the underlying iterator. Up to **10x faster**. - **Rust Migration**: Moved `try_find` implementation to Rust — Achieving **+15-30%** speedup across all use cases. - **Code Quality**: Various internal Rust code improvements for maintainability. - **Documentation**: Fixes on API reference generation. Website is now live again. --- ## 🐛 Critical Bug Fix ### Iter Performance Regression **Issue**: After migrating to abstract traits in *0.7.0*, the `Iter.__iter__()` method was accidentally removed. This caused Python to fall back to calling `__next__()` repeatedly when `Iter` objects were passed to functions expecting `Iterator` objects, resulting in **dramatically slower** performance. **Expected behavior**: `Iter` wraps any iterable and converts it to an iterator by calling `iter()` on it, storing the result in `self._inner`. When `Iter.__iter__()` is called, it should directly return `self._inner` rather than `self`. This delegation is crucial because: - It bypasses `Iter`'s Python-level `__next__()` implementation - The underlying iterator's native (often C-level) `__next__()` is called instead - This allows maximum efficiency regardless of the original iterable type (list, tuple, generator, etc.) **Fix**: Restored `Iter.__iter__()` to properly delegate to `self._inner`, restoring original performance characteristics. Also explicitly calls `__iter__()` in `PyoIterator` provided methods as additional safeguard. **Impact**: Any code passing `Iter` objects to functions that iterate over them (e.g., `list(iter_obj)`, `tuple(iter_obj)`, `func(iter_obj)` where func iterates) will see performance return to expected levels. --- ## 🚀 Performance Improvements ### try_find Migration to Rust Moved `Iter.try_find()` implementation from Python to Rust for better performance across all scenarios: ```text ┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓ ┃ Category ┃ Operation ┃ Rust (s, median) ┃ Python (s, median) ┃ Speedup ┃ ┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩ │ try_find │ find_at_middle │ 0.0067 │ 0.0087 │ 1.30x │ │ try_find │ find_at_end │ 0.0132 │ 0.0163 │ 1.24x │ │ try_find │ find_not_found │ 0.0148 │ 0.0168 │ 1.14x │ │ try_find │ find_error_late │ 0.0143 │ 0.0171 │ 1.20x │ │ try_find │ find_with_complex_predicate │ 0.0020 │ 0.0025 │ 1.23x │ └──────────┴─────────────────────────────┴──────────────────┴────────────────────┴─────────┘ Median speedup: 1.23x Rust wins: 5/5 ``` This act as a proof-of-concept for further migrations of iteration algorithms which are not calling Python `itertools`, builtins functions, or `cytoolz` functions (as those are already very efficient). ---

Release 0.8.30.8.3
? · 2026-01-13

See https://github.com/OutSquareCapital/pyochain/releases/tag/0.8.0 for change details.

Release 0.8.10.8.1
? · 2026-01-13

Fix build issues. See https://github.com/OutSquareCapital/pyochain/releases/tag/0.8.0 for last changes details

Release 0.8.00.8.0
? · 2026-01-13

# 🚀 pyochain 0.8.0 — Release Notes **Date:** 2026-01-13 📅 --- ## 🆕 Highlights - **Major Rust Migration** — `Option` and `Result` types completely rewritten in Rust using PyO3 - **3x-10x Performance Improvements** — Up to **10x** speedup on operations like `{Option, Result}.transpose`, **2-5x** on core use-cases like `Iter.map(Option)` or `Option == x`, and **no regression** on basic operations like `is_some()` or `unwrap()` - **Documentation Automation** — New tooling to verify exports and generate reference docs, which in turn allowed to fix various issues in the documentation - **Zero API Changes** — Drop-in replacement with same public API, no breaking changes --- ## ✨ New Features & Changes ### Rust-Backed Option & Result Complete rewrite of `Option[T]` and `Result[T, E]` types in Rust: - **Performance improvements** — Significant speedups on a variety of operations (see details below) - **Same public API** — No breaking changes, drop-in replacement - **First step towards more Rust implementations** — Lays the groundwork for future Rust implementations of other parts of the library ### Type System improvement - **`Ok.__new__()` and `Err.__new__()`** now infer the "opposite" type parameter as `Any` for better ergonomics. E.g `Ok(5)` is now inferred as `Result[int, Any]`. Since they now live in stubs, this open the door for more "hacks" like this to deal with Python type system limitations. ### Internal Improvements - **Improved test coverage** — Added comprehensive tests for Rust implementations - **Stubs testing** — Added a pytest plugin (from your dear maintainer) to ensure that docstrings in `.pyi` stubs are tested --- ## 📚 Documentation - **Automatic export verification tool** — New script who ensure that all documented classes/functions are exported, and automatically creates reference documentation from the codebase - **Fixed missing exports** — Several documented classes now properly exported in public API and documented - **Improved docstrings** — Added a few examples in docstrings during the migration --- ## 🚀 Performance Improvements Details Basic operations like `is_some()`, `unwrap()`, `xor()` show minimal changes, as they were already just attribute access or boolean checks in Python and can't really be optimized further. However, operations involving: - Higher-order functions (`map`, `and_then`, `or_else`) - Complex transformations (`flatten`, `transpose`, `unzip`) - Iterator integration (`filter_map`, chained operations) Show **significant speedups** thanks to Rust's optimized execution and reduced Python call overhead. Note that "complex transformations" involve in fact a few boolean checks and methods calls at *most*, so they were already quite fast in pure Python (as you can see below) But, this observation open the door for a lot more optimization and Rust ports in the future. The benchmark code can be seen in the commit `48770a7828e866be45fa7675d474c4b55704455d` ### Full Benchmark Results ```shell ┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓ ┃ Category ┃ Operation ┃ Rust (s, median) ┃ Python (s, median) ┃ Speedup ┃ ┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩ │ Instantiation │ Some(value) │ 0.0001 │ 0.0002 │ 2.27x │ │ Instantiation │ Dispatch to Some │ 0.0001 │ 0.0004 │ 3.28x │ │ Instantiation │ Dispatch to None │ 0.0001 │ 0.0001 │ 1.53x │ │ Equality Checks │ __eq__ │ 0.0001 │ 0.0002 │ 3.35x │ │ Equality Checks │ eq_method │ 0.0001 │ 0.0001 │ 1.74x │ │ Map with Closures │ map (identity) │ 0.0002 │ 0.0004 │ 2.44x │ │ Map with Closures │ map simple add │ 0.0002 │ 0.0004 │ 2.39x │ │ Chained Operations │ map -> filter -> map │ 0.0001 │ 0.0002 │ 1.72x │ │ Iter with Options │ Iter.map(Option) │ 0.0005 │ 0.0024 │ 4.41x │ │ Iter with Options │ Iter.filter_map (simple) │ 0.0044 │ 0.0093 │ 2.11x │ │ Iter with Options │ Iter.map -> filter_map -> map │ 0.0057 │ 0.0183 │ 3.22x │ │ Complex Methods │ flatten │ 0.0001 │ 0.0003 │ 4.93x │ │ Complex Methods │ unzip │ 0.0001 │ 0.0005 │ 4.32x │ │ Complex Methods │ zip │ 0.0001 │ 0.0003 │ 3.25x │ │ Complex Methods │ zip_with │ 0.0001 │ 0.0004 │ 2.49x │ │ Complex Methods │ transpose (Option->Result) │ 0.0001 │ 0.0010 │ 10.21x │ │ Complex Methods │ transpose (Result->Option) │ 0.0001 │ 0.0009 │ 8.98x │ └────────────────────┴───────────────────────────────┴──────────────────┴────────────────────┴─────────┘ Median speedup: 3.22x Rust wins: 17/17 ``` --- **Questions?** Report issues on [GitHub Issues](https://github.com/OutSquareCapital/pyochain/issues)

Release 0.6.60.6.6
? · 2026-01-09

# 🚀 pyochain 0.6.6 — Release Notes **Date:** 2026-01-09 📅 ## 🆕 Highlights - **`Iter.__bool__()` for lazy iteration checks** — Check if an iterator has elements without consuming them - **Peekable now implements `Checkable`** — Peeked values now returned as `Seq[T]` with truthiness checking - **Removed deprecated `Iter.empty()`** — Use `.new()` instead - **Improved documentation** — Enhanced trait descriptions and interoperability examples ## 🔄 API Changes & New Features ### New: `Iter.__bool__()` Method Added a `__bool__()` method to `Iter` for efficient emptiness checking without consuming elements: ```python >>> import pyochain as pc >>> it = pc.Iter([1, 2, 3]) >>> bool(it) # Check if iterator has elements True >>> it.collect() # All elements still available Seq(1, 2, 3) >>> empty_it = pc.Iter([]) >>> bool(empty_it) False ``` This method uses `itertools.islice()` and `itertools.chain()` to peek without consuming. #### `Checkable` Methods Now Work Correctly on `Iter` With `__bool__()` implemented, `Iter` now properly supports `Checkable` methods like `.then()` and `.ok_or()`: ```python >>> import pyochain as pc >>> pc.Iter([1, 2, 3]).then(lambda x: x.map(lambda v: v * 2).collect()) Some(Seq(2, 4, 6)) >>> pc.Iter([]).then(lambda x: x.map(lambda v: v * 2).collect()) NONE ``` #### Performance Consideration: Collect First When Appropriate While `__bool__()` enables proper `Checkable` behavior on iterators, **prefer collecting to a concrete collection first** if `Checkable` methods are applied within iteration: ```python >>> import pyochain as pc >>> >>> # ❌ Less efficient: __bool__() called on each iteration in the map >>> # This mean reconstructing the Iterator chain repeatedly >>> result = ( ... pc.Iter([1, 2, 3]) ... .filter_map(lambda x: pc.Iter(range(x)).then_some().map(lambda s: s.sum())) ... .collect() ... ) >>> result Seq(0, 1, 3) >>> # ✅ More efficient: collect first >>> result = ( ... pc.Iter([1, 2, 3]) ... .filter_map(lambda x: pc.Iter(range(x)).collect().then_some().map(lambda s: s.sum())) ... .collect() ... ) >>> result Seq(0, 1, 3) ``` Calling `__bool__()` on `Iter` reconstructs the iterator chain, and doing this repeatedly within `.map()` or similar iteration operations is costly. By collecting to eager collections (`Seq`, `Vec`, `Set`, etc.) beforehand, subsequent `Checkable` checks use `__len__()` which is a direct operation. ### Breaking: Peekable Refactored `Peekable` now inherits from `Checkable` and returns peeked values as `Seq[T]` instead of `Iter[T]`: ```python >>> import pyochain as pc >>> data = pc.Iter([1, 2, 3]).peekable(2) >>> data.peek # Now returns Seq directly Seq(1, 2) >>> data.values.collect() # Iter still includes peeked elements Seq(1, 2, 3) >>> # Checkable truthiness based on peeked values >>> data.then_some().map(lambda d: d.peek) Some(Seq(1, 2)) ``` **Migration:** Code using `.peek.collect()` should be updated to use `.peek` directly since it's already a `Seq`. ### Removed: `Iter.empty()` Deprecated Method The deprecated `Iter.empty()` method has been removed. Use `.new()` from the `PyoIterable` trait instead: ```python >>> import pyochain as pc >>> pc.Iter.new().collect() # Before: pc.Iter.empty() Seq() >>> pc.Seq.new() # Also works on other collections Seq() ``` Questions? Report issues on our [GitHub Issues](https://github.com/OutSquareCapital/pyochain/issues) page.

Release 0.6.50.6.5
? · 2026-01-09

# 🚀 pyochain 0.6.5 — Release Notes Date: 2026-01-09 📅 ## 🆕 Highlights • **Iter.unzip() & Iter.repeat() now fully lazy** — Memory-efficient evaluation using `itertools.tee()` • **PyoCollection mixin trait** — New shared collection trait • **Iter.from_ref() & .cloned()** — New lazy copying methods for efficient Iter branching • **Sorting refactor** — `Iter.is_sorted()` split into `Iter.is_sorted()` and `Iter.is_sorted_by(key=...)` • **Iter.try_collect() narrowed** — Now focuses on Option/Result types only for better type safety • **3 Breaking Changes** — See section below for migration guide ## 🔄 API Changes & New Features ### New Trait **PyoCollection** is a new mixin trait for all collection types (Seq, Set, Dict, Iter). Provides: - `.length()` and `.contains()` methods - Implementations for `__len__` and `__contains__` dunders methods - `collections.abc.Collection` inerhitance and valid implementation **Iter.is_sorted(key=...)** — Removed. Must use `Iter.is_sorted_by(key=...)` for key-based sorting checks. ```python >>> import pyochain as pc >>> pc.Iter([1, 2, 3, 4]).is_sorted() True >>> pc.Iter(["1", "2", "3"]).is_sorted_by(key=int) True ``` **Iter.try_collect()** — Now only accepts `Iter[Option[T]]` or `Iter[Result[T, E]]`. Removed support for `Iter[T | None]`. ❌ Before - now broken ```python import pyochain as pc pc.Iter([1, None, 3]).try_collect() ``` ✅ After - must use Option ```python >>> import pyochain as pc >>> pc.Iter([pc.Some(1), pc.Some(2), pc.Some(3)]).try_collect() Some(Vec(1, 2, 3)) >>> pc.Iter([pc.Ok(1), pc.Ok(2)]).try_collect() Some(Vec(1, 2)) >>> pc.Iter([pc.Some(1), pc.NONE, pc.Some(3)]).try_collect() NONE >>> # If you need old behavior, map to Option first >>> pc.Iter([1, None, 3]).map(pc.Option).try_collect() NONE ``` **Dict.contains_key()** — Replaced. Moved to `PyoCollection.contains()` for shared usability across all collection types. ```python >>> import pyochain as pc >>> d = pc.Dict({1: "a", 2: "b"}) >>> d.contains(1) True >>> pc.Seq([1, 2, 3]).contains(2) True >>> pc.Set({1, 2, 3}).contains(3) True ``` **repeat method** — Both methods repeat `Self` as elements in a new `Iter`, but differ in internal implementation: - `PyoCollection.repeat(n)` returns an `Iter[Self]` where each element is the entire collection repeated `n` times. This was the previous behavior of `Iter.repeat()`, which collected eagerly the data in an `hidden` way who might be confusing. - `Iter.repeat(n)` returns an `Iter[T]` where the elements of the original Iter are repeated `n` times in a fully lazy manner. ```python >>> import pyochain as pc >>> # Iter.repeat: repeats elements lazily >>> pc.Iter([1, 2]).repeat(2).map(list).collect() Seq([1, 2], [1, 2]) >>> pc.Seq([1, 2]).repeat(2).map(list).collect() Seq([1, 2], [1, 2]) ``` **Iter.from_ref() / .cloned()** — New lazy methods for efficient Iter copying using `itertools.tee()` ```python >>> import pyochain as pc >>> iter1 = pc.Iter([1, 2, 3]) >>> iter2 = pc.Iter.from_ref(iter1) >>> # Both can be consumed independently >>> iter1.take(2).collect() Seq(1, 2) >>> iter2.collect() Seq(1, 2, 3) ``` **Iter.empty() → Iter.new()** — Consolidated at `PyoIterable` level (deprecated) ```python >>> import pyochain as pc >>> pc.Iter.new().collect() Seq() >>> pc.Seq.new() Seq() ``` --- ## ⚡ Performance & Memory Improvements • **Iter.unzip()** — Now fully lazy using `itertools.tee()` instead of eager collection • **Iter.try_collect()** — Narrowed to Option/Result types only, reduced code paths and internal list optimization • **Iter.repeat()** — Fixed to be truly lazy with proper generator chaining --- ## 📚 Documentation Updates • Added `PyoIterable` and `PyoCollection` reference pages • Improved `Iter.product()` examples • Various docstring improvements and warning block formatting fixes ## 🎯 Upgrade Recommendations 1. ✅ Update `Iter.empty()` → `Iter.new()` if you use it 2. ⚠️ **Update `is_sorted(key=func)` → `is_sorted_by(key=func)`** — Breaking change 3. ⚠️ **Update `Iter.try_collect()` for Option/Result types only** — Breaking change, no more `U | None` support 4. ✅ Update `Dict.contains_key()` → `Dict.contains()` — Now unified across all collections 5. ✅ Consider using `Iter.from_ref()` and `.cloned()` for better lazy copying 6. ✅ Leverage `PyoCollection` trait if implementing custom collections --- Questions? Report issues on our [GitHub Issues](https://github.com/outsquarecapital/pyochain/issues) page.