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

RFC: Make function reassignment distinct #17399

Closed
ghost opened this issue Oct 4, 2023 · 2 comments
Closed

RFC: Make function reassignment distinct #17399

ghost opened this issue Oct 4, 2023 · 2 comments
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@ghost
Copy link

ghost commented Oct 4, 2023

In the wake of #1717, and in particular of the specific reason that #1717 was rejected, I think, let's lean into that.

The Observation

I think the reason people have an easy time thinking of functions as values, even in the context of Zig, is that they can well be:

// std/debug.zig
const print = warn;

Here is a function (the function itself, not a pointer to it) being passed around like any other comptime-only value, with no special annotation like a definition would have. Now, disallowing this would be silly, but the rejection of #1717 mentions that functions having explicit annotation as such at the definition level is actually a big bonus for Zig specifically, which this undermines.

The Solution

The idea is simple:

// std/debug.zig
fn print = warn;

It's the exact same semantics as the const case above, except a fn declaration can only declare a function; meanwhile, a const declaration loses that ability. Of course this only applies to functions themselves -- function pointers remain const or var, and fn cannot declare them. (Function definitions are unaffected.)

Pros

  • Greppability - if foo is callable, then grep 'fn foo' will find its declaration, whether that be a definition or a reassignment.
  • Inexpressible Insensibility - it is now impossible to declare a function identifier as var, as could have been done in status quo; as I remember that would have permitted some weirdness for no practical benefit.
  • Semantic Distinction - a function is very clearly a function, not any other kind of value; for the reasons noted in the rejection of RFC: Make function definitions expressions #1717, this is a concept we want to reinforce in the minds of authors.
  • (unimportant) Memory Type Faithfulness - the three declaration keywords now correspond to the three permission configurations for defined and loaded segments in object files: const for r--, var for rw-, fn for r-x. It doesn't matter but it's neat, maybe.

Cons

  • Complexity - by which I mean syntactic complexity, perhaps it poses problems for the parser (I don't know if it would but it might). I don't think it would, since we could just look for the first of ( or = to appear outside the context of a keyword, and that would tell us unambiguously what it is.
  • Inflexibility - functions now cannot sensibly be stored in an anytype identifier. It's not necessarily a huge deal as function pointers still can, but it's something to be aware of.
  • Friction - depending on your perspective, this may be a pro: if someone wants to do whatever comptime shenanigans, they now have to be aware whether the thing they're manipulating is a function or not. Although, they'd have to anyway, if they plan to call it; just like they'd have to explicitly mark a var if they plan to mutate it.
@ghost
Copy link

ghost commented Oct 5, 2023

This is not a bad idea per se, but it also feels like a solution looking for a problem.

Greppability - if foo is callable, then grep 'fn foo' will find its declaration, whether that be a definition or a reassignment.

Unclear benefit. Renamed functions are usually local to the file, so why would you need to grep for them? And then there's the language server.

Inexpressible Insensibility - it is now impossible to declare a function identifier as var, as could have been done in status quo; as I remember that would have permitted some weirdness for no practical benefit.

This is already a compile error.

Semantic Distinction - a function is very clearly a function, not any other kind of value;

True; but this lack of semantic distinction was a deliberate design choice, AFAIK. Rolling it back for functions only (but not for types and modules) would be kind of inconsistent.

Memory Type Faithfulness - the three declaration keywords now correspond to the three permission configurations for defined and loaded segments in object files: const for r--, var for rw-, fn for r-x. It doesn't matter but it's neat, maybe.

Interesting perspective, though I'd argue that fn is more like --x. Also, where would type aliases fit into this?

@Vexu Vexu added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Oct 5, 2023
@Vexu Vexu added this to the 0.13.0 milestone Oct 5, 2023
@ghost
Copy link
Author

ghost commented Nov 6, 2023

You know what, this is dumb. Identical semantics can be achieved with (semantic) function pointers and disallowing them would be dumb. It's syntactic complexity for nothing. If an object has extent in space, write it like that; otherwise the hard distinction loses meaning.

@ghost ghost closed this as completed Nov 6, 2023
@Vexu Vexu modified the milestones: 0.13.0, 0.12.0 Nov 8, 2023
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

1 participant