Skip to content

[stdlib] Add filter_types to variadic#5714

Closed
saviorand wants to merge 6 commits intomodular:mainfrom
saviorand:add-exclude-keep-types
Closed

[stdlib] Add filter_types to variadic#5714
saviorand wants to merge 6 commits intomodular:mainfrom
saviorand:add-exclude-keep-types

Conversation

@saviorand
Copy link
Contributor

A few convenience functions to make it easier to work with variadics

@saviorand saviorand requested a review from a team as a code owner December 28, 2025 12:02
@github-actions
Copy link

github-actions bot commented Dec 28, 2025

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@saviorand saviorand changed the title Add exclude and keep types to variadic [stdlib] Add exclude and keep types to variadic Dec 28, 2025
@saviorand
Copy link
Contributor Author

I have read the CLA Document and I hereby sign the CLA

modular-cla-bot bot added a commit to modular/cla that referenced this pull request Dec 28, 2025
@saviorand saviorand force-pushed the add-exclude-keep-types branch from e018854 to 5a6f21a Compare December 28, 2025 12:25
BEGIN_PUBLIC
[Stdlib] Add variadic type filtering utilities

Adds three new compile-time utilities to `Variadic` for filtering type sequences:

- `exclude_type[*types, type=T]` - Remove a single type from a variadic
- `keep_types[*types, keep_types=...]` - Keep only specified types
- `exclude_types[*types, exclude_types=...]` - Remove multiple types

These utilities enable ergonomic manipulation of `Variant` types and other
variadic type sequences at compile time.

Example usage:
```mojo
comptime Filtered = Variadic.exclude_type[*Variant[Int, String, Bool].Ts, type=Int]
comptime NewVariant = Variant[*Filtered]  // Variant[String, Bool]
```

Includes comprehensive tests demonstrating all three filtering modes and
chaining behavior.
END_PUBLIC
BEGIN_PUBLIC
[Stdlib] Improve variadic filtering API and add comprehensive tests

Updates the variadic filtering utilities to use variadic parameters for a more
ergonomic API, and adds comprehensive test coverage:

API improvements:
- Changed from `element_types=...` to `*element_types` variadic parameters
- Renamed `Trait` to `T` to match stdlib conventions (consistent with
  `Variadic.types`, `Variadic.slice_types`, etc.)
- Simplified parameter names: `type` instead of `exclude_type` for single exclusions

Usage is now more concise:
```mojo
// Before
Variadic.exclude_type[exclude_type=Int, element_types=MyTypes]

// After
Variadic.exclude_type[*MyTypes, type=Int]
```

Tests added:
- `test_exclude_type()` - Remove a single type from a variadic
- `test_keep_types()` - Keep only specified types
- `test_exclude_types()` - Remove multiple types
- `test_filtering_chained()` - Chain multiple filtering operations
END_PUBLIC
BEGIN_PUBLIC
[Stdlib] Replace specialized filters with generic filter_types

Replaces the three specialized filtering functions (`exclude_type`, `keep_types`,
`exclude_types`) with a single generic `filter_types` function that accepts a
user-defined predicate.

Implementation:
- Added `_TypePredicateGenerator[T]` - Generator type for predicates that take
  a type and return Bool
- Added `_FilterReducer` - Generic reducer that filters based on any predicate
- Added `Variadic.filter_types[*types, predicate=...]` - Public API

This design is more flexible and composable than having separate specialized
functions. Users can write any filtering logic they need:

```mojo
# Exclude a single type
comptime IsNotInt[Type: Movable] = not _type_is_eq[Type, Int]()
comptime Filtered = Variadic.filter_types[*MyTypes, predicate=IsNotInt]

# Keep only specific types
comptime IsNumeric[Type: Movable] = (
    _type_is_eq[Type, Int]() or _type_is_eq[Type, Float64]()
)
comptime Numeric = Variadic.filter_types[*MyTypes, predicate=IsNumeric]

# Exclude multiple types
comptime NotIntOrBool[Type: Movable] = (
    not _type_is_eq[Type, Int]() and not _type_is_eq[Type, Bool]()
)
comptime Filtered = Variadic.filter_types[*MyTypes, predicate=NotIntOrBool]
```

Tests include filtering to exclude single types, keep only specific types,
exclude multiple types, and chaining filter operations.
END_PUBLIC
@saviorand saviorand changed the title [stdlib] Add exclude and keep types to variadic [stdlib] Add filter_types to variadic Dec 29, 2025
@soraros
Copy link
Contributor

soraros commented Jan 7, 2026

@lsh Idea: can we make them conditional methods on Variadic itself (so chaining works etc)?

Copy link
Collaborator

@JoeLoser JoeLoser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this contribution, @saviorand! Filtering types is a useful ergonomic improvement for working with variadics. Before merging, I think we should resolve @soraros's #5714 (comment) about whether this should be a conditional method on Variadic itself. The current API:

  comptime step2 = Variadic.filter_types[*step1, predicate=IsNotInt]

vs potentially:

  comptime step2 = step1.filter[predicate=IsNotInt]

@lsh - could you weigh in on the preferred API direction?

Overall this is solid work - just want to nail down the API design before merging. Thanks again for the contribution!

@@ -305,6 +305,55 @@ def test_map_types_to_types():
assert_true(_type_is_eq[variadic[1], String]())


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion Can you add a test for filtering to an empty result (a predicate that matches nothing) to ensure that edge case is handled correctly.

@lsh
Copy link
Contributor

lsh commented Jan 10, 2026

@soraros I think that would be really nice, but there are some tradeoffs to consider. I would expect an API kind of like

VariadicIter[*elts].map[mapper].filter[pred].result

though this means that every use now needs to explicitly pull the elements out at the end. I think another important distinction is that this API would not be lazily evaluated. I'm still open to exploring that API though!

@soraros
Copy link
Contributor

soraros commented Jan 10, 2026

@lsh Yea, and we can of course land and iterate.

@saviorand
Copy link
Contributor Author

@JoeLoser @soraros thanks for the feedback! I definitely like the idea.
@lsh I guess this affects other functions in this file then, not just the filter? E.g will map_types_to_types also have to become a map method like this etc., for everything to stay consistent?
To keep this PR small I'd definitely like to focus on filter only, but this would mean we'll have two styles of API in the file, at least for a short while

@lsh
Copy link
Contributor

lsh commented Jan 10, 2026

I'd say we can land this and write the abstraction after. I have an idea I'll try out

@lsh
Copy link
Contributor

lsh commented Jan 10, 2026

!sync

@modularbot modularbot added the imported-internally Signals that a given pull request has been imported internally. label Jan 10, 2026
@ConnorGray
Copy link
Collaborator

This is super cool, nice work!

@lsh
Copy link
Contributor

lsh commented Jan 12, 2026

!sync

@modularbot
Copy link
Collaborator

✅🟣 This contribution has been merged 🟣✅

Your pull request has been merged to the internal upstream Mojo sources. It will be reflected here in the Mojo repository on the main branch during the next Mojo nightly release, typically within the next 24-48 hours.

We use Copybara to merge external contributions, click here to learn more.

@modularbot modularbot added merged-internally Indicates that this pull request has been merged internally merged-externally Merged externally in public mojo repo labels Jan 12, 2026
@modularbot
Copy link
Collaborator

Landed in 050840a! Thank you for your contribution 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

imported-internally Signals that a given pull request has been imported internally. merged-externally Merged externally in public mojo repo merged-internally Indicates that this pull request has been merged internally

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants