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

Make variable bindings accessible outside of auto_assert #1

Closed
zachallaun opened this issue Feb 21, 2023 · 3 comments
Closed

Make variable bindings accessible outside of auto_assert #1

zachallaun opened this issue Feb 21, 2023 · 3 comments
Labels
enhancement New feature or request

Comments

@zachallaun
Copy link
Owner

Currently, variables bound in the match pattern of an auto_assert are not accessible except in guards within that same assertion. For instance:

auto_assert pid when is_pid(pid) <- self()
pid # error, pid is not bound here

The work around is that the assertion returns the value, so if you really needed the result, you could do this:

pid = auto_assert pid when is_pid(pid) <- self()

Optimally, variables would be accessible in the block containing the assertion, as is the case with ExUnit's assert:

assert %{foo: foo, bar: bar} = some_call()
foo # foo and bar are available

How ExUnit does it

ExUnit statically extracts variables from the match expression and ensures that they are returned in the same order when the assertion is run. This is pseudo-code for demonstration purposes, but you can imagine the above assert example expanding to something like this:

value = some_call()
[foo, bar] = __assert_match__(value)
value

This means the assertion would return the value, which is desired, but would also bind the variables in the current scope.

Challenges for Mneme

The fundamental challenge is that Mneme doesn't know the vars that should be bound until runtime. Let's consider three cases:

# 1) new assertion
auto_assert self()

# 2) existing assertion
auto_assert pid when is_pid(pid) <- self()

# 3) incorrect assertion that will be updated
auto_assert pid when is_pid(pid) <- make_ref()

It turns out that cases 1) and 2) are possible to deal with. The second case can use the same technique that ExUnit uses, and the first can be handled by maintaining a sort of "private binding" that Mneme can draw from for future assertions. Since Mneme knows what new variables will be introduced by a new pattern, it can use those to e.g. pin that variable for future patterns in that test.

auto_assert pid when is_pid(pid) <- self()
auto_assert [^pid] <- [self()]

The third case is where things get really problematic, since a change in the value of an existing assertion can result in code that has a completely different set of variables. I'm not sure that there is an elegant solution to this.

I did some experimentation with this on this branch.

@zachallaun zachallaun added the enhancement New feature or request label Feb 21, 2023
@zachallaun
Copy link
Owner Author

zachallaun commented Feb 21, 2023

If this ends up being possible, we could also switch to using = instead of <- at that point. (One of the reasons I chose <- is to signal that the binding semantics are like with/for/etc.)

@tcoopman
Copy link

I would probably ignore the 3 case, as it can lead to any possible outcome.

@zachallaun
Copy link
Owner Author

One option for handling case 3 would be to always fail if an existing variable will no longer be present after updating, then prompt the user to re-run tests (or force-recompile and do it ourselves). If a variable was previously used that no longer exists, something will blow up.

@zachallaun zachallaun changed the title Variable bindings should be accessible outside of auto_assert Make variable bindings accessible outside of auto_assert Apr 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants