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

Merged
merged 24 commits into from
Feb 25, 2025

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 = a.into_iter().chain(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)

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.

please rebase - there is still some problematic issue with using an Iterator directly instead of an IntoIter as an arg - but for the time being should mostly work.

Reviewable status: 12 of 13 files reviewed, all discussions resolved (waiting on @hudem1)

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.

@hudem1 please rebase and check - it is ok not having tests with direct extrainto_iter() - it is a known bug (and it is actually nice not needing the extra "clipply" style warning for removing it) - but it seems to not really hinder us for the time being.

Reviewable status: 12 of 13 files reviewed, all discussions resolved (waiting on @hudem1)

@hudem1
Copy link
Contributor Author

hudem1 commented Feb 13, 2025

@hudem1 please rebase and check - it is ok not having tests with direct extrainto_iter() - it is a known bug (and it is actually nice not needing the extra "clipply" style warning for removing it) - but it seems to not really hinder us for the time being.

Reviewable status: 12 of 13 files reviewed, all discussions resolved (waiting on @hudem1)

Hi !

Sorry my late reply. I'll be more careful to reply faster!

I merged main into my branch, and removed the unnecessary into_iter if i understood correctly what you meant.

When i run the tests, i still have the following errors:

  • for 1st test test_iterator_chain_different_types:

1

  • for 2nd test test_iterator_chain_same_types:

2

It seems like the condition +TypeEqual<Self::Item, IntoIterU::Iterator::Item> in call to iterator::chain does not work well ?

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.

no need to appologize :) great work here.

thanks for the update - will continue looking into it now.

indeed meant removing the not required .into_iter() from the test.

Reviewed 8 of 9 files at r7, all commit messages.
Reviewable status: 12 of 13 files reviewed, 3 unresolved discussions (waiting on @hudem1)


corelib/src/test/iter_test.cairo line 2 at r7 (raw file):

use core::iter::PeekableTrait;
use core::ops::Range;

corelib/src/test/iter_test.cairo line 148 at r7 (raw file):

    let b: Range<u8> = 0..5;

    let mut iter = a.into_iter().chain(b);

Suggestion:

    let a: Array<u8> = array![7, 8, 9];
    let mut iter = a.into_iter().chain(0..5_u8);

corelib/src/iter/traits/iterator.cairo line 2 at r7 (raw file):

use crate::iter::adapters::{
    Chain, Enumerate, Filter, Map, Peekable, Zip, chained_iterator, enumerated_iterator, filter_iterator, mapped_iterator,

run ./scripts/cairo_fmt.sh.

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.

Thank you :)
Sure, let me know if there is anything else to modify!

Reviewable status: 11 of 13 files reviewed, 3 unresolved discussions (waiting on @orizi)


corelib/src/iter/traits/iterator.cairo line 2 at r7 (raw file):

Previously, orizi wrote…

run ./scripts/cairo_fmt.sh.

Done


corelib/src/test/iter_test.cairo line 2 at r7 (raw file):

use core::iter::PeekableTrait;
use core::ops::Range;

Done


corelib/src/test/iter_test.cairo line 148 at r7 (raw file):

    let b: Range<u8> = 0..5;

    let mut iter = a.into_iter().chain(b);

Done. I actually inlined a as well and in the other test as well

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 2 of 2 files at r8, all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @hudem1)

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.

you have other bugs - can you first makes sure the corelib itself compiles?
run ./scripts/cairo_test.sh.

Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @hudem1)


corelib/src/iter/traits/iterator.cairo line 722 at r7 (raw file):

    /// assert_eq!(iter.next(), Option::None);
    /// ```
    fn chain<U, impl IntoIterU: IntoIterator<U>, +TypeEqual<Self::Item, IntoIterU::Iterator::Item>>(

probably the cause for the actual failure.

Suggestion:

    fn chain<U, impl IntoIterU: IntoIterator<U>, +TypeEqual<Self::Item, IntoIterU::Iterator::Item>, +Destruct<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.

any updates?

Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @hudem1)

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.

please update - or tomorrow i will need to redo the PR.

Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @hudem1)

@hudem1
Copy link
Contributor Author

hudem1 commented Feb 23, 2025

please update - or tomorrow i will need to redo the PR.

Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @hudem1)

Oh no, late reply a 2nd time ! :(
I actually started an internship recently, and now my email inbox gets flooded by pipeline runs notifications, for every single pipeline that runs. This combined with advertisements I was already getting everyday makes me miss the important emails I wanna see like this project. I have to unsubscribe from all those to not miss anything. I'll make sure to do that before making new contributions ! :)

Anyway, it now compiles and tests pass, great!! Thank you for the fix!

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.

all good - thanks!
:lgtm:

Reviewed 3 of 3 files at r10, all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @hudem1)

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: :shipit: complete! all files reviewed, all discussions resolved (waiting on @hudem1)


a discussion (no related file):
@gilbens-starkware @TomerStarkware for 2nd eye.

@orizi orizi added this pull request to the merge queue Feb 24, 2025
@orizi orizi removed this pull request from the merge queue due to a manual request Feb 24, 2025
Copy link
Contributor

@gilbens-starkware gilbens-starkware left a comment

Choose a reason for hiding this comment

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

:lgtm:

Reviewed 4 of 13 files at r6, 6 of 9 files at r7, 3 of 3 files at r10, all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @hudem1)

Copy link
Collaborator

@TomerStarkware TomerStarkware left a comment

Choose a reason for hiding this comment

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

:lgtm:

Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @hudem1)

@gilbens-starkware gilbens-starkware added this pull request to the merge queue Feb 25, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 25, 2025
@gilbens-starkware gilbens-starkware added this pull request to the merge queue Feb 25, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 25, 2025
@orizi orizi added this pull request to the merge queue Feb 25, 2025
Merged via the queue into starkware-libs:main with commit fc40831 Feb 25, 2025
46 checks passed
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.

6 participants