When a function is called even before it is defined then there is no error reported. But if a macro or a variable is referred before its definition then I get errors “macro ‘func’ must be defined before this point but is defined later” and “undefined local variable or method ‘func’ for top-level” respectively as expected. Crystal version 1.0.0 & LLVM 10.0.0
1 puts func
2 def func
3 "function func"
Interesting question. Maybe this has to do with the sequence of compiling various types of code; kinda like the scope of a variable, but for methods, macros, etc.
Macros are methods that receive AST nodes at compile-time and produce code that is pasted into a program 1.
My guess would be that calling a method before it is defined is fine because the code it is evaluated at runtime but macro-stuff happens at compile-time (I could be wrong though).
I hope this make sense? :)
Yes, that’s more or less correct. In a first pass, the compiler processes all the types and methods (without their body) in the program (
TopLevelVisitor). Only after that, it starts processing top-level code (and the code inside methods). So a that point, it already knows which types and methods are available, regardless of the order of their definition relative to the code that uses them.
Variable definitions and reads are expressions and always execute in order. Hence you can’t read from a variable before it’s defined.
The thing with macros is a bit more complex. They are called like methods. From a syntactical point of view, there’s no difference between calling a method and calling macro. So it’s impossible to tell them apart. The compiler just checks if a macro by that name exists and if yes, treats it as a macro call.
Macro calls on the top level can define types, methods etc. - or even more macros. This all happens in the first stage (
TopLevelVisitor) and can have transitive dependencies (a macro defining another macro, for example). It’s impossible to know about all macro definitions in the entire program before expanding all macros. That creates a chicken and egg problem, and it is solved such that macros unlike methods are only visible after they are defined.
@straight-shoota Thanks for the detailed answer. This makes sense.