Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(corelib): core::iter::chain #7064

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

hudem1
Copy link
Contributor

@hudem1 hudem1 commented Jan 12, 2025

pub fn chain<A, B>(a: A, b: B) -> Chain<A, B>

Converts the arguments to iterators and links them together, in a chain.

Example

let a = array![1, 2, 3];
let b = array![4, 5, 6];

let mut iter = chain(a, b);

assert_eq!(iter.next(), Option::Some(1));
assert_eq!(iter.next(), Option::Some(2));
assert_eq!(iter.next(), Option::Some(3));
assert_eq!(iter.next(), Option::Some(4));
assert_eq!(iter.next(), Option::Some(5));
assert_eq!(iter.next(), Option::Some(6));
assert_eq!(iter.next(), Option::None);

@reviewable-StarkWare
Copy link

This change is Reviewable

Copy link
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

Reviewed 1 of 5 files at r1, all commit messages.
Reviewable status: 1 of 5 files reviewed, 3 unresolved discussions (waiting on @hudem1)


corelib/src/test/iter_test.cairo line 4 at r1 (raw file):

#[test]
fn test_iterator_chain() {

add test chaining different types.


corelib/src/iter/adapters/chain_adapter.cairo line 40 at r1 (raw file):

/// assert_eq!(iter.next(), None);
/// ```
pub fn chain<

shouldn't this be a method?


corelib/src/iter/adapters/chain_adapter.cairo line 44 at r1 (raw file):

    B,
    impl IntoIterA: IntoIterator<A>,
    impl IntoIterB: IntoIterator<B>[IntoIter: IntoIterA::IntoIter],

this seems to test the Iterators are of the same type - not their Items.

Code quote:

    impl IntoIterA: IntoIterator<A>,
    impl IntoIterB: IntoIterator<B>[IntoIter: IntoIterA::IntoIter],

Copy link
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

Reviewed all commit messages.
Reviewable status: 1 of 11 files reviewed, 4 unresolved discussions (waiting on @hudem1)


a discussion (no related file):
definitely do not edit the IntoIterator trait for this support.

Copy link
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 of 11 files reviewed, 5 unresolved discussions (waiting on @hudem1)


corelib/src/iter/adapters/chain_adapter.cairo line 49 at r2 (raw file):

    B,
    impl IntoIterA: IntoIterator<A>,
    impl IntoIterB: IntoIterator<B>[Item: IntoIterA::Item],

Suggestion:

    impl IntoIterA: IntoIterator<A>,
    impl IntoIterB: IntoIterator<B>,
    +TypeEqual<IntoIterA::IntoIter::Item, IntoIterB::IntoIter::Item>

@hudem1
Copy link
Contributor Author

hudem1 commented Jan 13, 2025

Reviewed all commit messages.
Reviewable status: 1 of 11 files reviewed, 4 unresolved discussions (waiting on @hudem1)

Previously, Orizi wrote...

a discussion (no related file): definitely do not edit the IntoIterator trait for this support.

Oh okay! I tried to do like Rust and thought IntoIterator trait might not have been fully finished. Sorry about that, I will revert the commit related to that and thank you for your suggestion after! I wasn't aware of this syntax.

@hudem1
Copy link
Contributor Author

hudem1 commented Jan 13, 2025

Reviewed 1 of 5 files at r1, all commit messages.
Reviewable status: 1 of 5 files reviewed, 3 unresolved discussions (waiting on @hudem1)


corelib/src/test/iter_test.cairo line 4 at r1 (raw file):

Previously, orizi wrote...

add test chaining different types.

Done.

@hudem1
Copy link
Contributor Author

hudem1 commented Jan 13, 2025

Reviewed 1 of 5 files at r1, all commit messages.
Reviewable status: 1 of 5 files reviewed, 3 unresolved discussions (waiting on @hudem1)


corelib/src/iter/adapters/chain_adapter.cairo line 40 at r1 (raw file):

Previously, orizi wrote...

/// assert_eq!(iter.next(), None);
/// ```
pub fn chain<

shouldn't this be a method?

I tried to do like the PR core::iter::zip #7050. In this PR, the zip is not created as a method to Iterator<T>. Am I missing something maybe ? Should I then go and add the method chain to Iterator<T> ?

Copy link
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 of 11 files reviewed, 5 unresolved discussions (waiting on @hudem1)


corelib/src/iter/adapters/chain_adapter.cairo line 40 at r1 (raw file):

Previously, hudem1 wrote…
Previously, orizi wrote...

/// assert_eq!(iter.next(), None);
/// ```
pub fn chain<

shouldn't this be a method?

I tried to do like the PR core::iter::zip #7050. In this PR, the zip is not created as a method to Iterator<T>. Am I missing something maybe ? Should I then go and add the method chain to Iterator<T> ?

i guess it should be the same answer for both.

the question is - why is it that way in rust, and should we keep the same - or do we have specific reasons to diverge.

@julio4
Copy link
Contributor

julio4 commented Jan 13, 2025

corelib/src/iter/adapters/chain_adapter.cairo line 40 at r1 (raw file):

Previously, orizi wrote…

i guess it should be the same answer for both.

the question is - why is it that way in rust, and should we keep the same - or do we have specific reasons to diverge.

So iter::adapters by definition takes iterator(s) to return a new iterator.
For most cases when it performs a transformation over a single iterator it makes sense to only have a method, i.e map: iter.map();
But when it concerns multiple iterators we can optionally export the adapters as a function as well, as the syntax is a bit more readable, i.e. zip: consider zip(iterA, iterB) vs iterA.zip(iterB). The first one is clearly understandable for example in for loops, the later one should still be possible for chaining iterator adapters (iterA.map().zip(iterB))

I think chain should be both a function and method of Iterator

Copy link
Contributor

@julio4 julio4 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 of 11 files reviewed, 5 unresolved discussions (waiting on @hudem1 and @orizi)


corelib/src/iter/adapters/chain_adapter.cairo line 49 at r2 (raw file):

    B,
    impl IntoIterA: IntoIterator<A>,
    impl IntoIterB: IntoIterator<B>[Item: IntoIterA::Item],

I believe we can't access directly the associated item without the trait impl, but something like this can work:

+TypeEqual<IntoIterA::Iterator::<IntoIterA::IntoIter>::Item, IntoIterB::Iterator::<IntoIterB::IntoIter>::Item>

I tried to implement chain as well before but I was stuck on this, I was not aware of this TypeEqual syntax, good to know!

Copy link
Contributor Author

@hudem1 hudem1 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 of 11 files reviewed, 5 unresolved discussions (waiting on @julio4 and @orizi)


corelib/src/iter/adapters/chain_adapter.cairo line 49 at r2 (raw file):

Previously, julio4 (Julio) wrote…

I believe we can't access directly the associated item without the trait impl, but something like this can work:

+TypeEqual<IntoIterA::Iterator::<IntoIterA::IntoIter>::Item, IntoIterB::Iterator::<IntoIterB::IntoIter>::Item>

I tried to implement chain as well before but I was stuck on this, I was not aware of this TypeEqual syntax, good to know!

Yes indeed, I tried orizi's suggestion and there was a slight Path problem.

Thank you for your suggestion @julio4! I tried and believe we can omit ::<IntoIterA::IntoIter> and ::<IntoIterB::IntoIter>, and using the code below seems to work:

Code snippet:

+TypeEqual<IntoIterA::Iterator::Item, IntoIterB::Iterator::Item>

Copy link
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 of 11 files reviewed, 4 unresolved discussions (waiting on @hudem1 and @julio4)


corelib/src/iter/adapters/chain_adapter.cairo line 40 at r1 (raw file):

Previously, julio4 (Julio) wrote…

So iter::adapters by definition takes iterator(s) to return a new iterator.
For most cases when it performs a transformation over a single iterator it makes sense to only have a method, i.e map: iter.map();
But when it concerns multiple iterators we can optionally export the adapters as a function as well, as the syntax is a bit more readable, i.e. zip: consider zip(iterA, iterB) vs iterA.zip(iterB). The first one is clearly understandable for example in for loops, the later one should still be possible for chaining iterator adapters (iterA.map().zip(iterB))

I think chain should be both a function and method of Iterator

i think it shouldn't be a function, in zip as well - as a place holder for when we have proper inline macros.

chain! and zip! for an arbitrary number of arguments are much better than binary chain and zip.

Copy link
Contributor

@julio4 julio4 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 of 11 files reviewed, 4 unresolved discussions (waiting on @hudem1 and @orizi)


corelib/src/iter/adapters/chain_adapter.cairo line 40 at r1 (raw file):

Previously, orizi wrote…

i think it shouldn't be a function, in zip as well - as a place holder for when we have proper inline macros.

chain! and zip! for an arbitrary number of arguments are much better than binary chain and zip.

For arbitrary number of arguments it would be better I agree.
Is the inlining macro system not good enough to be able to do this now?
Should we keep these function or remove them for now?

Copy link
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 of 11 files reviewed, 4 unresolved discussions (waiting on @hudem1 and @julio4)


corelib/src/iter/adapters/chain_adapter.cairo line 40 at r1 (raw file):

Previously, julio4 (Julio) wrote…

For arbitrary number of arguments it would be better I agree.
Is the inlining macro system not good enough to be able to do this now?
Should we keep these function or remove them for now?

not good enough now - but i hope will be better soon enough.

i think we should remove now, as we can always add, but removing after any release is much harder.

@hudem1
Copy link
Contributor Author

hudem1 commented Jan 13, 2025

I still got an error in the Iterator::chain method that I need to solve before the PR being ready. (And I also need to rebase).

By the way, if you got an idea as to why the error appears, I am interested!

Screenshot 2025-01-13 at 23 10 27

Copy link
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

Reviewed 1 of 5 files at r1, 9 of 11 files at r3, all commit messages.
Reviewable status: 10 of 12 files reviewed, 6 unresolved discussions (waiting on @hudem1 and @julio4)


corelib/src/iter/traits/collect.cairo line 95 at r3 (raw file):

/// Trying to convert an already existing iterator into an iterator
/// just returns the iterator itself

seems really unrelated to PR.

Code quote:

/// Trying to convert an already existing iterator into an iterator
/// just returns the iterator itself

corelib/src/iter/adapters/chain_adapter.cairo line 8 at r3 (raw file):

pub struct Chain<A, B> {
    // These are "fused" with `Option` so we don't need separate state to track which part is
    // already exhausted, and we may also get niche layout for `None`.

WDYM?

Code quote:

and we may also get niche layout for `None`.

corelib/src/iter/adapters/chain_adapter.cairo line 12 at r3 (raw file):

    // Only the "first" iterator is actually set `None` when exhausted.
    a: Option<A>,
    b: Option<B>,

if the second one is never None - let us:

Suggestion:

    // These are "fused" with `Option` so we don't need separate state to track which part is
    // already exhausted, and we may also get niche layout for `None`.
    //
    // Only the "first" iterator is actually set `None` when exhausted.
    a: Option<A>,
    b: B,

corelib/src/iter/adapters/chain_adapter.cairo line 49 at r3 (raw file):

        self.b = Option::Some(second_container);

        value

Suggestion:

        // First iterate over first container values.
        if let Option::Some(first) = self.a {
            if let Option::Some(value) = first.next() {
                self.a = Option::Some(first);
                return Option::Some(value)
            } else {
                self.a = Option::None;
            }
        }

        // Then iterate over second container values.
        self.b.next()

corelib/src/iter.cairo line 3 at r3 (raw file):

mod adapters;
mod traits;
pub use adapters::{Chain, chained_iterator};

this is only for usage in the trait - right?
if so:

Suggestion:

pub use adapters::Chain;
pub(crate) use adapters::chained_iterator;

Copy link
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

this is possibly is just the LS - unless you get this error when compiling as well now.

Reviewable status: 10 of 12 files reviewed, 5 unresolved discussions (waiting on @hudem1 and @julio4)

Copy link
Contributor

@julio4 julio4 left a comment

Choose a reason for hiding this comment

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

Reviewed 1 of 11 files at r3.
Reviewable status: 11 of 12 files reviewed, 6 unresolved discussions (waiting on @hudem1 and @orizi)


corelib/src/iter/adapters/chain_adapter.cairo line 0 at r3 (raw file):
After removing fn chain you can rename the module to only chain instead of chain_adapter

Copy link
Collaborator

@orizi orizi left a comment

Choose a reason for hiding this comment

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

Reviewed 1 of 11 files at r3.
Reviewable status: all files reviewed, 6 unresolved discussions (waiting on @hudem1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants