You effectively delayed the expansion of @crayon_str because @macroexpand expands to a call on an input expression:
julia> @macroexpand begin
import Crayons: Crayon, @crayon_str
f() = @macroexpand crayon"dark_gray"
end
quote
#= REPL[13]:2 =#
import Crayons: Crayon, @crayon_str
#= REPL[13]:3 =#
f() = begin
#= REPL[13]:3 =#
Base.macroexpand(Main, $(QuoteNode(:(crayon"dark_gray"))), recursive = true)
end
end
However, the expand call is inside the function block, and itβs not constant folded so it got delayed to the method calls:
julia> @code_warntype f()
MethodInstance for f()
from f() in Main at REPL[1]:3
Arguments
#self#::Core.Const(f)
Body::Any
1 β %1 = (:recursive,)::Core.Const((:recursive,))
β %2 = Core.apply_type(Core.NamedTuple, %1)::Core.Const(NamedTuple{(:recursive,)})
β %3 = Core.tuple(true)::Core.Const((true,))
β %4 = (%2)(%3)::Core.Const((recursive = true,))
β %5 = Core.kwfunc(Base.macroexpand)::Core.Const(Base.var"#macroexpand##kw"())
β %6 = (%5)(%4, Base.macroexpand, Main, $(QuoteNode(:(crayon"dark_gray"))))::Any
βββ return %6
Another workaround is to run the import statement before the begin block is parsed, nesting it by interpolating an @eval call:
julia> @eval begin # fresh REPL session
$(import Crayons: Crayon, @crayon_str) # $(nothing)
f() = crayon"dark_gray"
end
f (generic function with 1 method)
julia> @code_warntype f()
MethodInstance for f()
from f() in Main at REPL[1]:3
Arguments
#self#::Core.Const(f)
Body::Crayon
1 β return \e[90m
This is also how I like to compute constants without declaring const globals before nesting them in an expression, e.g. @eval log5(x) = log2(x)/$(log2(5)) (that is not a good example because itβs simple enough for the compiler to constant fold). Still, I personally prefer running the import statements separately, inserting text ruins the point of copy and pasting into begin blocks in the REPL.
nice workaround. I am curious whether this also works for Distributed, i.e. whether the Module and/or macro can be easily serialized. (At least it is probably not the recommended way to send full modules over the wire).
(My concrete case is about Pluto, i.e. I am using Malt.jl actually, currently in Distributed-fallback-mode)