Why define a const use method is possible, but it not work when use a local variable

Following code work

X = hello

def hello
  "hello"
end

p X # => "hello"

But following code not work.

hello = "hello"
X = hello
p X

Get following error:

In 1.cr:2:5

 2 | X = hello
         ^----
Error: undefined local variable or method 'hello' for top-level

As far as I understand, both cases should not work, because const definition is compile-time, and calling method is run time, right?

[…] const definition is compile-time, and calling method is run time, right?

That is correct, but I think you may be misunderstanding. While constants are defined at compile-time, they are not assigned at compile-time. The return type of methods are also defined at compile-time, which is why you can declare a constant to be the result of calling a method, however, the method will only be invoked during run-time (and only if the constant is used).

This means you can declare a constant to be the result of calling gets! :see_no_evil: The call will occur when either the constant is first referenced or when the constant is assigned (though there still needs to be a reference later in the code).

Y = gets # <- Won't be called as there is no reference to Y.

puts "This will print *before* we set X"

print "Enter a value for X: "
X = gets

puts "This will print *after* we set X"
puts "So what was X? #{X}"
puts "The method isn't called twice: #{X}"

puts "\n-----------\n\n"

puts "PI = #{PI}"

puts "Before PI assignment line"

PI = 3.1415

The reason you can’t assign a local variable to a constant is because local variables are – according to the language referece – only defined when you assign them a value, which happens during run-time.

Hope that clears it up :slightly_smiling_face:


Slightly off topic, but it may also be worth knowing that constants in Crystal aren’t transitive. What this means is that while constants cannot be reassigned, the data they reference can still be modified. Consider the following example:

PRIME_NUMBERS = [2, 3, 5, 7, 11]

puts PRIME_NUMBERS # => [2, 3, 5, 7, 11]
PRIME_NUMBERS << 13
puts PRIME_NUMBERS # => [2, 3, 5, 7, 11, 13]
2 Likes