Delete assert-return RFC

(this analysis should have been done before the RFC but the thought never crossed my mind)

There's 63K assertions in all luarocks repositories combined. Out of this, around ~600 assertions would be broken as a result of this change. This is ~1% which is pretty uncomfortable (I was hoping for a couple of odd unit tests) - as such this RFC is going to get closed. We will maintain the current behavior of assert and try to adapt type checker to be reasonably useful instead.

The common thread between all of these cases (many of them target custom APIs although some target core APIs like string.match) is that assert is used together with a function that either returns nil (for errors), or multiple arguments. Under these conditions multi-arg assert is useful.

A couple examples:

```
out_r, out_w = assert(unix.pipe())
local header, body = assert(data:match "(.-\r\n)\r\n(.*)")
local z85_secret_key, z85_public_key = assert(zmq.curve_keypair())
local _, r = assert(coroutine.resume(co, msg))
```
This commit is contained in:
Arseny Kapoulkine 2021-05-04 22:15:14 -07:00 committed by GitHub
parent cbc99c3222
commit fb67f5760c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 0 additions and 61 deletions

View File

@ -1,61 +0,0 @@
# Change `assert` to return one value
> Note: this RFC was adapted from an internal proposal that predates RFC process
## Summary
Change `assert` to only return the first argument on success
## Motivation
Today `assert()` function has confusing semantics:
`assert(x)` fails if `x` is falsy and returns `x` otherwise (this is okay!)
`assert(x, y)` fails with `y` as the error message if `x` is falsy, and returns `x, y` otherwise (why?)
`assert(x, y, z)` fails with `y` as the error message if `x` is falsy, and returns `x, y, z` otherwise (why???)
It's not clear what purpose is there behind returning more than one argument from `assert`, as it doesn't seem like it can be useful.
Specifically, when a two-argument form is used, the second argument must be an error message (otherwise if the first argument is falsy, the error behavior will be confusing); if it *is* in fact an error message, it's not clear why it's useful to return that error message when the first argument was truthful.
When a three-argument form is used, the third argument is just ignored and passed through, again for no apparent benefit.
## Design
This proposal argues that long term it's cleaner for us to fix the semantics here to just return the first argument. Any extra arguments will be ignored at runtime as usual, but our type checker can start treating these as invalid.
To be more precise, we'd be formally switching from
```
declare function assert<T...>(...: T...): T...
```
to
```
declare function assert<T>(value: T, error: string?): T
```
This allows us to align the runtime behavior of `assert` to the type definition and reduces the chance of an accidental error - since all return values are forwarded when the call is used as the last function argument, this code transformation is safe:
```
- foo(bar)
+ foo(assert(bar))
```
But this code transformation may be unsafe if `foo` has optional parameters:
```
- foo(assert(bar))
+ foo(assert(bar, "bar should not be nil"))
```
After this proposal the transformation becomes safe.
## Drawbacks
This may break user code. Unfortunately there is only one way for us to find out if it does, which is to roll this change out. We will use release notes to communicate the change and, if concerns are raised by the community ahead of time, we will investigate them.
## Alternatives
Alternatively we could keep `assert` behavior exactly as it is; this would match mainline Lua but we want to make the language better and if we were designing the standard library again we'd never make this choice, hence the proposal.