begin
raise "foo"
$var = "aaa"
rescue
end
p $var
This crashes at runtime. I assume it happens because the type of $var
is guessed as String
. As not all codepaths lead to an assignment, it should actually be String?
. Or not be guessed at all with an error message.
Adding $var : String?
fixes the crash.
Shall we remove global variables once and for all? :-)
It does complain when the variable is anything but global :)
module A
begin
raise ""
@@a = "a"
rescue
end
p @@a
end
Error class variable '@@a' of A is not nilable (it's String) so it must have an initializer
I'm not against removing then, only note is that it will make writing small, quick and dirt scripts less quick (but also less dirt).
The problem with global variables is that they are, well, global, so they can be used anywhere and they don't have a specific point where they are declared and initialized. With class variables this is more controlled: they belong to a class, and they must be initialized there, and if they are not initialized there then it's easy to figure out that they are nilable.
Also, once we make crystal work with multiple threads, global variables will always need some kind of lock so it'll be pretty bug-prone to use them. Class variables are more controllable, because one can't access them anywhere, and inside a class one could provide locking or concurrency mechanism to prevent data races.
Global variables are implemented and useful in many languages, PHP, Ruby, Python, or even we can use extern
in C.
Maybe we can set the type of global variables always be T | Nil
(that we need to do #.not_nil!
by ourself or just do something with Nil
supported operations), or just do assert on every read-operation?
But I have no idea with the multi-thread solution.
Scoping a class-variable in a module, or type, has the same practical use as a global var, so it's not like we'd be without them. Since these always goes through getters and setters, you will after LLVM-optimization have a straight memory access if that's all they do. If you later parallelize your application you can simply add locking to the getters and setters and it just works. With globals you'd have to rewrite them to classvars. So, I think using globals scoped under a module/type, as classvars, and thereby always accessing via getter/setter, would be a good thing.
Setting globals to always be T | Nil
seems very dodgy imo.
Global vars are no more, so I'll close this
Most helpful comment
Shall we remove global variables once and for all? :-)