-
-
Notifications
You must be signed in to change notification settings - Fork 794
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
Bool assert idea #3828
base: main
Are you sure you want to change the base?
Bool assert idea #3828
Conversation
19f2f5a
to
74d85bf
Compare
@hayleigh-dot-dev and @giacomocavalieri have raised concerns with people using I feel that introducing and documenting the feature purely in the context of testing would likely get us where we want to be, looking at Erlang and Elixir and Rust as examples of this succeeding elsewhere. I suspect most folks don't know you can even use their assertions outside of tests. As additional encouragement we can also add another layer of nagging to gleam publish, and show when a package panics on the package index or some other documentation. It was suggested we could ban panicking in published libraries, but there are lots of valid reasons to panic in libraries, even if typically it is discouraged and a bad idea. For example, OTP based code. It was suggested that |
Do you have any examples? Looking at gleam_otp the only place where it's used is doing some In the tests and examples there's a couple of places doing - let assert True = process.is_alive(p1)
+ assert process.is_alive(p1) And there's also a couple of other assertions that could be rewritten but I'm not sure I like it: - let assert Error(Nil) = process.receive(subject, 10)
+ assert result.is_error(process.receive(subject, 10)) - let assert Error(Timeout) = task.try_await(t3, 35)
+ assert Error(Timeout) == task.try_await(t3, 35) What would become the preferred style in those cases? If we're not making |
I don't have examples to hand, but it would be any case there a precondition is checked. The error checking example you have there should be rewritten like this: assert process.receive(subject, 10) == Error(Timeout) Now |
But then if I want to change it in the future and assert that it's just an error do you think it would be easier to go this way: - assert process.receive(subject, 10) == Error(Timeout)
+ let assert Error(_) = process.receive(subject, 10) Or this way: - assert process.receive(subject, 10) == Error(Timeout)
+ assert process.receive(subject, 10) |> result.is_error To me the second one feels encouraged. And it's a bit unexpected that writing |
Why do you think that? To use equality rather than a helper function is most common way to write that test both in Gleam and the other languages I'm familiar with.
Perhaps unintuitive, but easy to learn, and it's the norm and doesn't cause problems for any of the other languages referenced. Equality is the simpler and more common feature so I don't think people would default to skipping over it in favour of the more complex partial pattern matching. |
I'm in favor of this. I think the existing I don't think it needs to be blocked from use in non-test code, but I do expect it will lead to folks requesting a "disable assertions in production builds" feature. |
I think you can discourage the second one by having whatever updated test runner that replaces the current gleeunit use the left hand side as the expected value in messaging about failures. |
Would it be possible to pull in the argument names, instead of 1, 2, 3 in this case?
|
Argument names are not part of the public API of a function. |
How about labelled arguments? |
What would you propose the exception data structure be in the presence of labels? |
I don't really know enough about the internals of Gleam to gauge the feasibility of this at any level, but in addition to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a very good idea, and the design is pretty near what we will eventually implement. I've left a couple of small notes to potentially consider inline.
This does have the file and line information, but has no message or information | ||
about the values. We are planning to add support for a custom message to `let | ||
assert`, at which point is could have the same amount of information as `case` + | ||
`panic` approach, but with a slightly more concise syntax. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have now added a custom panic message for let assert
, so it might be worth rewording this paragraph with that in mind
| Key | Type | Value | | ||
| --- | ---- | ----- | | ||
| value | t1 | the value the expression evaluated to at runtime | | ||
| kind | Atom | `literal` or `expression` | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to include the original source code of the expression in here? So we can display stuff like age + 1: 27
I guess it may not be necessary if the entire assert
ed expression is displayed above, but something to potentially consider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the code would be ideal as it can be arbitrarily large, which could result in code size bloating.
It could have byte indexes perhaps? Currently it contains the source line. That with a parser would be enough to get the source code, but what's best I am unsure. Perhaps my suggestion below of including the actual code isn't ideal.
Reading this again I don't understand why there's kind: expression | literal
. What does the programmer do with this information?
| gleam_error | Atom | See individual errors | | ||
| message | String | See individual errors | | ||
| module | String | The module the error occured in | | ||
| function | String | The function the error occured in | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tables seem to be written from the perspective of the representation on the Erlang target (e.g. Atom doesn't exist in JS). Is it worth clarifying how these will be represented on JS, or is it just assumed that we'll use the nearest JS equivalent of the Erlang-specific stuff?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup on JS it's the same but atom -> string
What do you think?
rendered