-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
use let
keyword instead of const
for immutable variable declarations
#181
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
Comments
Also relevant is #83. "...if we had this different return value syntax, it implies that we should support tuples..." If we had tuples, then we may consider following Rust's lead and having |
Can we support both? Like, |
I think removing const will make at least some people angry, so ideally we need both, |
Having two ways to do the same thing goes against the principle of One Way To Do Things, which exists to support the goal of readability. Uniform code is more readable. It's OK to have both the
Not quite. I'm considering removing the
Both Swift and Rust use |
That would actually be perfect imo! |
From an ML perspective, For tuples, do we really want to allow assigning mutable and immutable data in the same statement? That seems like a little much. // This is too busy.
let (mut a, b) = foo()
// These are clean.
let (a, b) = foo()
var (c, d) = bar() I don't like two keywords for assigning a variable. |
JavaScript now has You most likely want to use I suggest zig to copy Scala on this: use |
The letters are too visually similar... One last character determines the whole chain of surprising consequences...
|
I've updated the patch, trivially made an exception for git clone https://github.com/andrewrk/zig Zig
cd Zig
git checkout f18e34c
cd ..
wget https://gist.githubusercontent.com/procedural/9443f1a5353e158a4422ecc397a96740/raw -O zig_const_to_let.patch
cd Zig
patch -Np1 < ../zig_const_to_let.patch
mkdir build
cd build
CC=clang CXX=clang++ cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $(cc -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | cc -E -x c - -v 2>&1 | grep -B1 "End of search list." | head -n1 | cut -c 2-) -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $(cc -print-file-name=crtbegin.o))
make && make install && cd lib/zig/ && find -type f -print0 | xargs -0 sed -i s_"const"_"let"_g && cd ../.. |
Let's make a language table for how to declare a single assignment variable.
Any others we should add? |
Implicit declaration is an option. a = 7; And we could use a keyword for immutables. const a = 7; |
Now it's no different than assignment, isn't it? :) |
I veto implicit declaration. First of all if there were implicit declaration the default should be immutable, not mutable. And then as far as parsing goes, it's easier to parse for both humans and computers if there is a keyword to indicate that a constant is about to be declared. And then importantly, in this situation: var a: i32 = 0;
fn f() {
const a = 7;
} We get a compile error saying that we shadowed |
|
There is also the option of using different operators for immutable/mutable declaration/assignment. For example: Immutable declaration (the most common/most desirable case):
Mutable declaration (more cumbersome to type, to discourage heavy use):
Assignment (overwriting the value, more "special" than regular immutable declaration):
Go uses |
@xyproto This is not what C programmers would expect... I wouldn't mind getting rid of But I feel like discussions like these deserve another topic (hey, there's also |
One thing I'd like to add is that let a = 7;
let b = 6;
var c = 3;
var d = 7;
let e = 5 + 4; This wouldn't make a difference if we used Nim's multi-line-variable-declaration. var
x, y: int
a, b, c: string
const
x = 1
y = 2
z = y + 5 |
Playing devil's advocate here, maybe not stacking them up is better for readability. Now the sneaky |
One thing to think about:
We also use If we used Flipping things around, there is the Rust approach, where things are constant by default, and you have to explicitly declare mutability. In this case you could do |
@andrewrk how about
That's what I thought too... |
Current plan:
|
Since In short: Should not the |
@thejoshwolfe do you have any thoughts on what @xyproto proposed? |
I think that |
rather than "mutable pointer to a u8", say "pointer to a mutable u8". now the order makes sense (and it's also the correct description). I think the only reason people instinctively think of the phase "const pointer" is confusion caused by C's terrible syntax and terminology. I'm nervous about making implicitly typed parameters easier to declare than explicitly typed parameters. then the programmers who think you shouldn't use semicolons in javascript will argue that you shouldn't declare parameter types in zig. is that what we want? do we want haskell-style optional parameter type restrictions or do we want a way to declare any-type parameters? effectively equivalent, but different paths of least resistance. |
I don't mind changing const to let but I feel like a simple instruction within the documentation that explains the relevance of constant variables would be just as efficient in guiding the programmer to doing the right thing. I feel like If zig were to change const to let, var would be equally as reachable, and let mut would be a syntax complexity hint to the programmer (like usingnamespace is) towards its mutability. |
One other area where the noun vs. verb distinction between
The latter feels quite awkward to me. |
I wanted to also point out that function params (except pointers) are const by default, which makes sense but is different then mutable by default every where else. I would like to see const by default, if we could drop const or let on variable definition ( := ) that would be the best. a := true;
b : u8 = 1;
mut c := 1; Also how do you make a const pointer to const data? []const const u8 ?
const []const u8 ? If const by default then how do you make a mutable pointer to mutable data? mut [] mut u8 ?
[]mut u8 mut ? |
@cshenton I feel. It can be reordered to read "Let [public] [mutable] let BlackHole = struct{};
let pub WhiteHole = struct{};
let DEFAULT_ANGLE: f64 = 1.02;
let mut PREFERRED_ANGLE = DEFAULT_ANGLE;
let pub rot13 = fn (name: [:0]mut u8) void {};
let pub mut handler: ?fn (reason: [:0]u8) void = null; This has appeal. A downside is that the whole left column is 3 characters, so you can't scan for mutables as easily as with const/var. (See This probably screws a little with the accepted destructuring proposal. let x, y, mut z, s.field = blk: {
break :blk 1, 2.2, 'c', &val;
}
Having used |
Just to add my two cents. I'm personally for const rather than let, but of course it's not a deal breaker. I've been told a couple of times that "you should use const there where you used var", and it's not like I used var because it's simpler to type. I was just lazy about constness, or didn't quite understand how/where to use const properly. Changing it to let wouldn't have helped. Const feels more "telling". I feel like the meaning of "let" is only clear if you already know it from some other language, or if you're mathematically inclined perhaps. But there's nothing in particular about the word that conveys the meaning in the same way const does. That both let and var are three letters, I mean sure, it makes it look a bit more consistent if you have many declarations in a row? Otherwise I feel like it's actually a plus that const looks different - it becomes easier to notice. Useful for code reviews if nothing else. |
My suggestion of := is based on the idea that we already have : type = in the language. I would like to see var only used for the generic type or something better(I think jai does $name though another symbol would be better for non-us keyboards). @hryx Googling golan short variable declaration problems, the only complaints I found are related to variable shadowing when used in multiple assignment. Which I think is what you might have pointed out. |
I don't like this. When first seeing zig, the difference between |
Half baked idea carried from #4107 (comment) Swapping between
Default everything to const and append a separate keyword for mutability, e.g. Edit: with assigned anon functions being the new norm, I'm not sure I like this idea because all struct methods would have to be |
I think the grammar status quo is quite good, but changing
let foo : i32 = 10; // will never be mutated
var bar : i32 = 20; // will maybe be mutated
var bam : *const i32 = &bar; // the pointer may point to some mutable data, but may not be mutated via the pointer itself |
My arguments for keeping This issue has been Open for 3.3 years now. That's around 1212 mornings where no project member with the power to do so woke up with a firm decision made in their gut to go "let over const: yeah let's do this, it's the needful & righteous move". So just by that metric it maybe never was such an appealing notion. My few cents as a newcomer very enthusiastic about the current Zig lang that (after a few years of my observation from the fence) seems to have taken great care to adhere to its founding principles and prevent the ever-present danger of "fluff creep" that comes with growing community / adoption: there's really no pain in More crucially, keeping Let-over-const proposal / discussion / possibility arguably also either doesn't serve or somewhat collides to some degrees with these Zens, though this is a subjective take:
Not to flamebait or cause endless mudslinging over each others' interpretations of these mantras 😆 just a peaceably-like reminder to not lose sight there |
There is a nice symmetry between |
I think that However if it is desired to have immutable variable declaration shorter than mutable it would make sense to change Another alternative is |
As far as readability goes, I think |
I agree with @metaleap and @Rocknest - maybe leave Maybe We're really talking about "immutable variables" and "mutable variables" here. I have seen "variables" be confusing to students, especially those who had a good grasp on math, but who didn't understand the imperative programming model. (I am not a teacher, just someone who's done some minor tutoring a dozen or so times before). Naming them But maybe this is getting a bit pedantic. At the least, renaming If the |
Zig is going to throw recognisability for C-programmers out the window with #1717 so I don't think that's a big concern. @metaleap |
@kavika13 There is a strange stereotype that |
As a bias F# fan, I'm in favor of dropping |
One thing that I don't like about C is that pointers are mutable by default. When you define a function signature in C, you always should prefer const pointers because they tell the user of your API that the data won't get modified. If you look up the function signatures of something like Zig partially solves this problem by making function arguments
So I think pointer/slice types being mutable by default is bad, since you shouldn't be able to modify data unless you explicitly ask to. My proposalSo putting this together with #4107, I'd propose this system:
I added point 5 because zig already allows "static" variables inside functions if you wrap them in a |
@momumi I can agree with all this. I predict a little friction from people who are used to mutable being the default, but I personally buy into "mutability needs to be auditable, and thus explicit", even in an imperative language. Also, I think the keyword should be Edit: I split this into two replies, one regarding mutability, one regarding storage specifiers, so people can react to them separately |
@momumi I think this throws the thread off track. This thread discusses mutable vs immutable, and throwing storage specifiers in here just tosses a wrench in the works. I don't disagree with being able to properly address storage specifiers, but I am not sure that it should be bundled in with this particular issue. I think it can be discussed separately, and doesn't have to be tied together. I think this is especially true if you don't conflate In fact, IMO, you could have keywords for all those things, and specifying more than one keyword at a time could make everything less confusing, even if some of the combos are weird. I.e. if
Yeah, I think we should make that more explicit too. A variable acting "global" inside a struct, without some sort of explicitly auditable syntax for it, I think is a mistake. I think making that more explicit could ease the seeming need for this static storage specifier syntax. That's also potentially getting quite off-topic. IMO, the issue you linked is a better place to discuss that - #4107 Anyhow, I think storage specifiers can and should be discussed separately. I think they shouldn't be conflated with mutability/immutability. Unless people disagree with me, then I think we should stop discussing them on this thread. Edit: I split this into two replies, one regarding mutability, one regarding storage specifiers, so people can react to them separately |
Yeah, you probably right, I guess what I wanted to bring up is how
I think the more keywords you can chain together the harder it becomes to read and write (what order do you use?). It also allows for redudancy: |
Similar to what others have said previously in the thread, I think if, as I said in my comment, there was no
I don't think Zig
Even better reason to not use
The redundancy in those examples could be a compiler warning, if it seems important. Same with examples like yours above, if As for
|
At the end of the day it should be a compiler error to declare So if it comes down to preference, I put my vote behind the more concise system because it's quicker to read and still conveys the same information. I don't care that much if we use
I don't think the use of the word variable is the main problem here, I'd guess it's more the concept of equality. In math when you see
Yeah,
Zig already gives an error when you use With the only use cases being I'm not the best at communicating, so sorry if I'm coming across as argumentative. Anyway have a great day :) |
Yup, I agree on that point. Those are all recognizable mnemonics for mutable assignment. I think
Yeah, I think that's fine! I care most about whether I can grep for values that support mutable assignment. If it's I suspect it is possible and not actually hard to do. We can verify it by someone exhaustively enumerating the possible qualifiers in all their contexts, and seeing if there's any holes. If the list for that grep is very short, that would also be nice. 3 keywords isn't so bad. 1 would be awesome, but I can sacrifice that if said grep is contractually guaranteed by the language, and well documented :) If the language is also arranged in a way that keyword soup doesn't have to happen unless I'm doing very questionable and hard to audit things (in which case keyword soup will help with the audit), then that'd also be nice. IMO it takes second priority, though it is worth considering. In this particular case, I don't think the two goals are at odds.
Same to you as well! Hope I'm not being argumentative, hope you have a good day :) |
Once again after careful consideration I'm landing on status quo. Thanks everybody for your input and apologies for the design churn. |
Currently, we have
const
andvar
for variable initializations.const
means that the bytes directly referenced by the variable cannot change after this assignment.var
means that they can.Previously we had copied Rust and had
let
andlet mut
. See #34.@procedural has suggested renaming
const
tolet
.@thejoshwolfe says:
One argument for
let
overconst
is that it's the same length asvar
and thus as easy to type. In general the language is supposed to guide the programmer into doing the Right Thing, which is to default to usingconst
/let
for as many things as possible instead ofvar
.One argument for
const
is that it matches the syntax of C, C++, and JavaScript.In this issue, let's figure out How It Should Be and make a decision. Then once the decision is made we can refer people to this issue.
The text was updated successfully, but these errors were encountered: