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

Methods not found when using lambdas #6802

Open
nventuro opened this issue Dec 13, 2024 · 6 comments · May be fixed by #7088
Open

Methods not found when using lambdas #6802

nventuro opened this issue Dec 13, 2024 · 6 comments · May be fixed by #7088
Assignees
Labels
bug Something isn't working

Comments

@nventuro
Copy link
Contributor

nventuro commented Dec 13, 2024

Aim

The code below is a simplification of something I was attempting when I run into compiler errors:

pub unconstrained fn call_lambda_on_vec<Env>(
    foo: BoundedVec<Field, 5>,
    lambda: fn[Env](BoundedVec<Field, 5>) -> ()
) {
    lambda(foo);
}

unconstrained fn main(param: BoundedVec<Field, 5>) {
    call_lambda_on_vec(param, |vec| {
        // I try to call random BoundedVec methods, but they don't work
        assert(vec.len() > 2);
        assert_eq(vec.get(0), vec.storage()[1]);
    });
}

This errors out with

error: No method named 'len' found for type 'BoundedVec<Field, 5>'
   ┌─ src/main.nr:11:16
   │
11 │         assert(vec.len() > 2);
   │                ---------
   │

error: No method named 'get' found for type 'BoundedVec<Field, 5>'
   ┌─ src/main.nr:12:19
   │
12 │         assert_eq(vec.get(0), vec.storage()[1]);
   │                   ----------
   │

error: No method named 'storage' found for type 'BoundedVec<Field, 5>'
   ┌─ src/main.nr:12:31
   │
12 │         assert_eq(vec.get(0), vec.storage()[1]);
   │                               -------------
   │

I'm not sure what to infer from this, since the example is quite minimal. I imagine the issue would become evident when debugging.

@nventuro nventuro added the bug Something isn't working label Dec 13, 2024
@github-project-automation github-project-automation bot moved this to 📋 Backlog in Noir Dec 13, 2024
@TomAFrench
Copy link
Member

Seems weird but what happens when you provide a typo hint inside of the lambda?

@guipublic
Copy link
Contributor

Seems weird but what happens when you provide a typo hint inside of the lambda?

It works:

pub unconstrained fn call_lambda_on_vec<Env>(
    foo: BoundedVec<Field, 5>,
    lambda: fn[Env](BoundedVec<Field, 5>) -> ()
) {
    lambda(foo);
}

unconstrained fn main(param: BoundedVec<Field, 5>) {
    call_lambda_on_vec(param, |vec:BoundedVec<Field, 5>| {
        // I try to call random BoundedVec methods, but they don't work
        assert(vec.len() > 2);
        assert_eq(vec.get(0), vec.storage()[1]);
    });
}

@asterite
Copy link
Collaborator

It seems what's happening is that vec in the lambda doesn't have a type (it's an unbound type variable) when then len method is searched, so it's not found. But type-checking continues and eventually the compiler figures out that vec is a BoundedVec. Once the error is shown, the type variable is bound so the type is BoundedVec and that's why the error is "No method named 'len' for 'BoundedVec'".

@jfecher
Copy link
Contributor

jfecher commented Dec 13, 2024

@asterite is right, we don't "push down" types at all during type checking. We only infer from the bottom up. This often means lambda arguments in particular will need type annotations if you're calling methods or accessing fields in the lambda.

@asterite
Copy link
Collaborator

I was wondering how Rust solves this, because it works in Rust.

But if you have this:

pub struct Foo {}

impl Foo {
    fn foo(self) {}
}

fn main() {
    let lambda = |foo| foo.foo();
    lambda(Foo {});
}

then it doesn't compile (it asks you to put a type annotation on |foo|).

If you have this it compiles fine:

pub struct Foo {}

impl Foo {
    fn foo(self) {}
}

pub fn call(_f: fn(Foo) -> ()) {}

fn main() {
    call(|foo| foo.foo());
}

So I can imagine Rust sees that call's argument has a function type, and uses that to give a type to foo in |foo|. But it gets tricky because this also works in Rust:

pub struct Foo {}

impl Foo {
    fn foo(self) {}
}

pub fn call<T>(_t: T, _f: fn(T) -> ()) {}

fn main() {
    call(Foo {}, |foo| foo.foo());
}

and even this:

pub fn call<T>(_f: fn(T) -> (), _t: T) {}

fn main() {
    call(|foo| foo.foo(), Foo {});
}

but not this:

pub fn call<T>(_f: fn(T) -> (), _g: fn() -> T) {}

fn main() {
    call(|foo| foo.foo(), || Foo {});
}

@jfecher
Copy link
Contributor

jfecher commented Dec 13, 2024

Rust uses bidirectional type checking, so it pushes types down as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: 📋 Backlog
Development

Successfully merging a pull request may close this issue.

5 participants