In #1585 has_constant?
was added and allows you to check if a constant is defined within a type. But I think there is no hay to check if a top-level constant exists.
Adding a top-level macro method to check if the constant is defined in the internal Program
type that holds the top level could look like:
{{ has_constant?(:MyShard) }}
Or if we expose the top level as an alias we could directly use.
{{ Program.has_constant?(:MyShard) }}
It can be useful to check if a shard is required or not. And to use feature checks instead of comparing versions.
I think the latter would be better, as this is also an issue with like "getting methods on the top level", etc.
Actually, you can do this right now :-D
{% puts @type.has_constant?(:Int32) %}
{% puts @type.has_constant?(:Int256) %}
https://play.crystal-lang.org/#/r/8qfs
@type
at the top-level refers to the program.
That said, it's true that there's no way to explicitly refer to the top level...
But maybe we can add @type.top_level
to access the top level from any type? That way we don't need to replicate all of the methods on TypeNode
on the top level.
Ahhh yes, the issue was more so having access to top level constants/methods when the macro itself is _NOT_ in the top level as well.
Then I still think it makes sense to expose the top level as @type.top_level
. Program
_could_ work but it can be quite confusing if there's an actual type called Program
.
How about another "macro instance var" like @toplevel
? having it as a method on @type
somehow irks me since it's not really a property of a type node. And indeed Program
isn't very explicit about what it is either.
Yeah, another macro instance var sounds good too.
@program
?
Why Program
? Seems strange to tie the name to a badly named god object in the compiler. Top-level is what we call it everywhere else.
That being said, the way you reference top-level functions in crystal is ::puts
. How about ::has_constant?
Program is probably a leak from the compiler internals. @top_level
sounds about right.
The main benefit of @top_level
vs ::has_constant?
is that this will allow other methods defined in TypeNode
to be applied to the top level.
@bcardiff ::
should refer to the top-level type, and it should be a TypeNode
. Just like String.has_constant?
works right now, but with ::
referring to the top level.
I don't follow @RX14. If ::
is a value, then the syntax should be ::.has_constant?
. If ::
works in the same way as in the non-macro land, then is not a value; but a syntax that affect the constant resolution.
I'm just saying make ::foo
access foo
on the top-level type node, just like String.foo
would.
I initially thought it could be a good idea. However, if you do this at the top level:
{% has_constant? :Foo %}
{% ::has_constant? :Foo %}
my initial thought would be that they are equivalent, since we are already at the top-level. So making ::
mean "but call it on the top level" is a bit confusing.
I think introducing a @top_level
macro instance var is clearer.
@asterite why wouldn't they be equivalent? At the top level those should be equivalent. ::
is only solving the problem of making the top-level accessible when you're not at the top-level scope.
But that means that in the compiler we'll have to do this:
Crystal::Macros
. Like debug
, puts
, etc.I think it's a bit confusing. Specially if later on there's a TypeNode method that has the same name as ones in the top-level right now.
Most helpful comment
How about another "macro instance var" like
@toplevel
? having it as a method on@type
somehow irks me since it's not really a property of a type node. And indeedProgram
isn't very explicit about what it is either.