Been thinking about module based object construction a bit and was wondering what the Crystal community’s take was for modules being able to overwrite ivars in the types that inherit them, and how people get around dealing with the possibility of this happening. I know that this can technically happen with methods too, and I think that’s a whole other can of worms to look at but, ivars aren’t really documented by crystal docs
so the only way you know you might be colliding with one is by reading the code, various macro techniques, or accidentally colliding.
Basic Example:
module A
@myvar = 100 # Error: instance variable '@myvar' of C must be Int32, not (Float64 | Int32)
end
module B
@myvar = 100.0
end
class C
include A
include B
@myvar = "One hundred"
end
puts C.new.@myvar
Full error with trace:
Error target sandbox2 failed to compile:
In src\sandbox2.cr:2:3
2 | @myvar = 100 # Error: instance variable '@myvar' of C must be Int32, not (Float64 | Int32)
^-----
Error: instance variable '@myvar' of C must be Int32, not (Float64 | Int32)
Float64 trace:
src\sandbox2.cr:6
@myvar = 100.0
The error is also a bit cryptic not telling the user which module this error is coming from, just the line the instance variable is defined, the expected type, and the including type, just not the included type.
There are some obvious workarounds to this but nothing 100% concise or concrete:
module A
# Fresh variables but we have to wrap it in a macro def
macro make
@%my_var = 100000
def my_var
@%my_var
end
end
make # macro call
# All of these could still be overwritten by accident, just more and more unlikely
@name_this_variable_very_esoterically= 100
@_even_more_weird = 1000
@_____________________yeah_we_can_do_this = 10000
@_UUID_helpful_name = 1000000
end
What’s everyone’s take?