-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Require extra parentheses to disambiguate calling a function pointer from a container field #6405
Comments
I am definitely in favor of removing BoundFn, but the extra parens aren't necessary for disambiguation. I like that they make function pointer calls explicit, but they could be a little awkward lexically. For example, if const func = foo.bar;
func(); // is this allowed?
(func)(); // or does it need to be this? If the parens are only required when the function pointer is a struct member, that seems like a really special case. What about a function pointer as a global var? Does that need parens? But if the parens are required always, then what about Since we're planning to make a distinction in the type system between function pointers and functions (#1717), we can almost force this distinction in a more straightforward way by requiring const Foo = struct { bar: *fn () void };
const foo: Foo = ...;
foo.bar(); // Error, bar is a function pointer, must dereference to call
foo.bar.*(); // No error
const baz = foo.bar; // No error
baz(); // Error: baz is not a function, use baz.*
baz.*(); But this doesn't actually work. const Foo = struct { bar: *fn () void };
const foo: Foo = ...;
foo.bar(); // Error, bar is a function pointer, use .()
foo.bar.(); // No error
const baz = foo.bar; // No error
baz(); // Error: baz is not a function, use baz.()
baz.(); // No error
|
To be clear, the only special case is
I don't understand how you can still get rid of BoundFn, because: const Foo = struct { fn bar() void {} };
const foo: Foo = ...;
const baz = foo.bar; // Is this an error? |
What I meant to point out is that the extra parens are not necessary for the compiler, we are choosing to enforce them as an extra step in order to make the code easier to read. This is fine, but I question whether it really makes the code easier to read if the extra syntax is only required in specific cases that are dependent on surrounding context, and the extra syntax is valid in other cases but has a different meaning.
This could be weird when const ptrs = struct {
// global runtime function pointer
pub var globalPtr: *fn() void;
// instance runtime function pointer
instPtr: *fn() void;
const globalFunc = fn() void {
globalPtr(); // case 1: no x.y, call to function pointer looks like normal call
};
const instFunc = fn(self: *@This()) void {
(self.instPtr)();
};
};
const foo2 = fn() void {
ptrs.globalPtr(); // case 2: fails compile? function pointer
(ptrs.globalPtr)(); // case 3: success
ptrs.globalFunc(); // case 4: success? If the remapping is unilateral, this would fail.
(ptrs.globalFunc)(); // case 5: success?
var x: ptrs = undefined;
x.instPtr(); // case 6: fails compile, function pointer
(x.instPtr)(); // case 7: success
x.instFunc(); // case 8: success
(x.instFunc)(); // case 9: fails compile, x has no field instFunc
}; This has the desirable behavior that cases 6 and 9 both fail, but the undesirable behavior that cases 2 and 5 are different, and that case 2 is different from case 1. You could instead decide that 2 and 5 both succeed, but now parens no longer tell you that the function may be a function pointer. I think if you can't easily make this distinction, it's not worth enforcing the parens. In other words, if we're going to go to the trouble of requiring special syntax for invoking a function pointer, we should require that for all function pointers, regardless of the surrounding context.
Yes, this would parse successfully but fail to compile, just like in your proposal. |
Hmm. I hadn't considered this, and I think this basically kills this specific syntax-based proposal. |
I propose that
<expr>.field(<args>)
become special syntax for calling@TypeOf(<expr>).field(expr, <args>)
.No other syntax is changed, so
(<expr>.field)(<args>)
can still be used to call a function pointer.This has two benefits:
Rust does this (or something very similar).
Note that this is not UFCS (#148):
The text was updated successfully, but these errors were encountered: