Module Variables
Describe the feature
Weeks ago, on V's Telegram channel, I had proposed the idea of introducing module variables.
The objective of this idea is to remove __global from the compiler completely, since module variables could replace its function (in a safer and more idiomatic way).
Global variables in V require a flag to work (-enable-globals) and can be accessed from anywhere in the code, plus they are always mutable, making them somewhat unsafe to use.
Additionally, with the implementation of module variables, you could make const stop receiving values that are computed in runtime, because static would fulfill that function perfectly. Then const would receive pure values that can be computed in comptime.
My suggestion is to use the static keyword (already used to declare static variables inside functions) to declare module variables, which can be declared as pub and mut as needed:
// report/report.v
module report
pub static mut errors = int(0)
// main.v
module main
import report
fn main() {
report.errors += 1
}
Use Case
In addition to what has already been said, the idea is also to make a distinction for constants, which currently receive all types of values (both runtime and comptime), which could confuse new users who are learning V, but come from other languages where const receives pure values that are computed in comptime.
Proposed Solution
No response
Other Information
No response
Acknowledgements
- [X] I may be able to implement this feature request
- [X] This feature might incur a breaking change
Version used
latest
Environment details (OS name and version, etc.)
anywhere
[!NOTE] You can use the 👍 reaction to increase the issue's priority for developers.
Please note that only the 👍 reaction to the issue itself counts as a vote. Other reactions and those to comments will not be taken into account.
Like a singleton pattern? Would be able to be shared among threads?
I like the idea of consts being comptime only.
I don't like having mutable globals in the language, not hidden behind ugly keyword and a compiler flag.
As a user and upon reading this, have various thoughts about it.
1) Thought the point of keeping global variables was low level and compatibility issues with other languages.
If that is the case, then wouldn't various V users want them to be "accessed from anywhere in the code"? For the turned on global variables to act and be similar to the situation that people are use to in other low level languages... Not quite sure how module variables would be received, when V's __global is as easily identifiable as const, even for newbies.
2) My understanding is that team V wants to discourage both global and static variable usage.
Some time ago (upon me starting to play with V), I posted this discussion, #13926. The sentiment was for functions to use structs at the module level. Static variables only within functions and inside unsafe.
However, it can be argued that the documentation fails to make this usage clear, with examples of structs replacing usage of global and static variables. This might be leading to confusion or even frustration, because it's a different (even if arguably better) way. I do want to make it clear that I've become accustomed to V's way, and agree with the reasoning, but I do think team V may want to be mindful of the difference for people coming in from other languages.
Module variables could be used to handle C global variables perfectly, and this could also fix issue #22691, making it so that a module variable can be identified as external and thus used correctly in the code.
module main
@[c_extern('gVar')]
static mut c_var int
fn main() {
c_var += 1
println(c_var)
}
Of course, module variables could also be shared safely between threads, as long as the variable is not mutable.
Is this a feature now or not yet?
Globals have their use, but this language not having a module level private mutables, is decisive and could make me rework my project to another language.
There's no way I have to write a getter/setter and an intrincated named global variable just to expose some variable, not only harder than C, but slower (20 more assembly instructions minimum per variable access, heavy).
I like this approach using static keyword because it's C-like idiomatic.
Hope that this comes to a commit soon so I can advance in my game engine.
Module level variables are not unsafe or unparallelizable, the junior can learn to use it safely with just a two paragraph page in docs.vlang.io, and for parallelization just atomic fetchs shall do trick.
I think that this design of getting rid of module level variables is unasintomathically killing my beloved V, and it's leading whereareas no one likes.
Maybe it's just me that I don't know how to use it, if so, please update docs because I spent 4 hours reading them I did't found anything
Warmest regards, Bruno.
CC: @medvednikov
PS: Please don't make me go back to Vala, I dislike GLib.
@bruneo32 V has a hidden static keyword you can use. Works just like in C. We implemented it for C2V'ed code.
also why not just use __global?
you prefer static because it's more clean?
Also I guess we can make __global module scoped. Will break some code, but it seems to be a cleaner solution. @spytheman what do you think?
You know me... I think __global should be harder to use, not easier.
Those who can't wrap their heads around not using globals, or are unwilling to put up with the extra steps to do so, should probably look at a different language.
And yes, static is simpler to use, you don't need any extra command line arguments to enable it, etc. - but at least it's namespaced, not total program-level globals.
Hi everyone, I mean to use some kind of module level variable that can be mutable I don't need to access it from another module; for example player.v can have player data variables and just expose functions like draw, update, create, etc. I want to set variables on create and modify them in the update, but if I declare them as unsafe static I cannot access it from the other functions.
Using globals in this scenario is anoying because I have to declare the variables prefixed for each file like player_x instead of just x.
I like globals a lot for some unsafe cases like the unethical but useful global I have __global null = unsafe { nil }, I wouldn't get rid of it.
The static approach mentioned at the description of this issue would help me a lot.
But if there's a secret key to use the static keyword as you say I would like to try it. Is there any example?
Thanks!
fn foo() {
unsafe {
static x := 0
x++
println(x)
}
}
fn main() {
foo()
foo()
foo()
}
v run a.v
1
2
3
I know that kind of static inside a function like C, but I mean at module level.
@medvednikov our proposition here is to have an experimental flag like -enable-module-statics, so to declare variables like Go's module var using the static keyword for this purpose.
Also for the previous conversation, globals should not disappear because they serve a purpose, the risk is taken with the ugly keyword + compiler flag.
Note: This is just a simple example and can be solve in other ways but the necessity for this kind of module variable would be clearer with more examples. Don't stick to it and think more ones
Example:
// game/game.v
pub struct Room {
pub mut:
create ?fn ()
update ?fn (delta f32)
draw ?fn ()
}
// This is hell, but globals should not disappear for it
__global null = unsafe { nil }
// This are effectively scoped globals because they are public
pub static mut room_current := &Room(null)
pub static mut room_goto := &Room(null)
// rooms/room0.v
import game
pub const room0 = &game.Room{create, update, draw}
// Module scoped variable (not public)
// Uninitialized maybe possible (?)
static mut x := int(0)
fn create() {
// Reset each room restart
x = 32
}
@[unsafe]
fn update() {
// Pseudo code, it could be gg or sdl input event
if keydown(keycode.space) {
x++
}
if x >= 64 {
// End game
game.room_current = null
}
}
fn draw() {
// Pseudo code again
draw_rect(x-8,y-8,x+8,y+8)
}
// main.v
module main
import game
import rooms
fn main() {
game.room_current = rooms.room0
mut last_room := null
for !isnil(game.room_current) {
// Use intermediate variable so the user
// cannot destroy the room until the end of the loop
room := game.room_current
// Only if the room has change
if last_room != room && room.create != none {
room.create()
}
// Always, delta just simulated for this example
if room.update != none {
room.update(0.016)
}
if room.draw != none {
room.draw()
}
// Update last room
last_room = game.room_current
// Goto another room
if !isnil(game.room_goto) {
game.room_current = game.room_goto
game.room_goto = null
}
}
}
Using scoped globals could reduce the use of global variables and sanity of the general V coding. Currently instead of game.room_current I use a global game_room_current, but for repetitive files like rooms, player, entities, etc; I dislike the use of globals because I would have to name each variable prefixed with the file name, like in C.
Making polymorphism for this Room structure is actually a workaround, but it would introduce an overhead that I don't want, and that's not the point of this language.
If there's a design style encouraged to overcome this issue, it should be clear on the docs, because I didn't read anything regarding it.
Have a nice day, and sorry for the inconvenience.
Note:
static mutcould be substituted for another keyword likevar/__var/..., since a static without a mut is just a module constantconst.
CC: @StunxFS
@bruneo32
Note: static mut could be substituted for another keyword like var/__var/..., since a static without a mut is just a module constant const.
No, what I proposed was not only to add support for variables at the module level, but also to stop const from supporting values that are computed at runtime. Therefore, you can't replace static mut with var and leave const as it currently is.
A static non-mut is very different from a const. consts SHOULD only handle comptime values.
There is also no need to implement a flag to use static.
I though they were comptime
No, consts are runtime, since they can be set by calling a function. That can't happen until runtime.
Can it detect when constants are not dependant of a runtime variable like const num = int(5) should be #define num 5? It would be a nice feature I think
@bruneo32 Yes, CGen generates #define when the value is compile-time.
Cool, but returning to the main question, are we having module mutables in a near future?
Undecided, so far.