# "Illegal" macro names

**URL:** <https://discourse.julialang.org/t/illegal-macro-names/128136>\
**Category:** General Usage\
**Tags:** question\
**Created:** [April 16, 2025, 6:32pm UTC](https://discourse.julialang.org/t/illegal-macro-names/128136 "2025-04-16T18:32:45Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [April 16, 2025, 6:32pm UTC](https://discourse.julialang.org/t/illegal-macro-names/128136/1 "2025-04-16T18:32:45Z")

</div>

I am wondering if there is any reason to avoid macro names which the parser would normally reject? For example, in BorrowChecker.jl, I want to define the `@&` macro:

```julia-repl
julia> @& Vector
Union{Vector, Borrowed{<:Vector}, LazyAccessor{<:Vector, P, S, <:Borrowed} where {P, S}}

julia> @& :mut Vector
Union{Vector, BorrowedMut{<:Vector}, LazyAccessor{<:Vector, P, S, <:BorrowedMut} where {P, S}}

```

> **\[this looks nicer in the REPL\]**
>
> ![image](https://global.discourse-cdn.com/julialang/original/3X/9/e/9e8618e4fa53df0522e5ca3bd93336e5bbe4aa6d.png)

This is essentially a shorthand to the `OrBorrowed{T}` alias which behaves like `&T` in Rust (and `&mut T` respectively). When integrating BorrowChecker.jl with a library, you tend to need to write `OrBorrowed{...}` a _LOT_, as a way of extending your functions to take immutable references to objects when running the borrow checker. The shorthand makes this easier and more readable.

However, when you try to define this normally, you get:

```julia-repl
julia> macro &(expr)
           esc(expr)
       end
ERROR: ParseError:
# Error @ REPL[3]:1:7
macro &(expr)
# └─────┘ ── Invalid signature in macro definition

julia> macro Base.:&(expr)
           esc(expr)
       end
ERROR: syntax: invalid macro definition around REPL[1]:1
Stacktrace:
 [1] top-level scope
   @ REPL[1]:1

```

But you can get around this with an `eval`:

```julia-repl
julia> @eval macro $(:&)(expr)
           esc(expr)
       end
@& (macro with 1 method)

```

I am just wondering if there’s any issues with this, or if I am completely fine to define this otherwise invalid macro.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [April 16, 2025, 6:47pm UTC](https://discourse.julialang.org/t/illegal-macro-names/128136/2 "2025-04-16T18:47:19Z")

</div>

It’s not an illegal macro name; `:(@&)` is a valid `Expr`, though this alone isn’t a test of valid syntax e.g. `:(macro &() end)`, and attempted evaluation throws an `UndefVarError` instead of a `ParseError`.

I think this is just the parser being unable to intuitively handle symbols for infix operators; specifically for `&`, there seems to be missing `:call` and `:block` sub-`Expr`s, which also causes definitions for functions to fail e.g. `&() = 0`. Parentheses works, e.g. `macro (&)() end`. I wish there were some sort of guideline for when `:` and `()` are needed, it always takes me by surprise.

The syntax error for `macro Base.:&` is a separate [issue #54488](https://github.com/JuliaLang/julia/issues/54488) for module qualification, rooted in little historical demand or support for extending macros, let alone across modules. That exact code wouldn’t make sense either because there is no `@&` macro in Base, nor is it related to the function `Base.:&`.

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [April 16, 2025, 8:11pm UTC](https://discourse.julialang.org/t/illegal-macro-names/128136/3 "2025-04-16T20:11:28Z")

</div>

Thanks!

P.S., I’m assuming it’s a different story for reserved keywords? I was thinking about defining `@let` which gives this error:

```julia-repl
julia> macro let(ex) end
ERROR: ParseError:
# Error @ REPL[2]:1:7
macro let(ex) end
# └─┘ ── invalid macro name

```

But similarly you can also get it working with an `@eval`:

```julia-repl
julia> @eval macro $(:let)(ex) end
@let (macro with 1 method)

```

The idea here being you could have something more Rust-like for BorrowChecker.jl:

```julia
@let x = [1, 2, 3]
@let :mut y = [4, 5]
@lifetime lt begin
    @let rx = @& ~lt x
    @let ry = @& ~lt :mut y
end

```

which is about as close as you can reasonably get to

```rust
let x = vec![1, 2, 3];
let mut y = vec![4, 5];
let rx = &x;
let ry = &mut y;

```

other than the need for the explicit `@lifetime` scope in Julia.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [April 16, 2025, 8:19pm UTC](https://discourse.julialang.org/t/illegal-macro-names/128136/4 "2025-04-16T20:19:27Z")

</div>

Slightly more straightforwardly, you can avoid the eval with the `var""` string macro, which is how you can use anything as an identifier:

```Julia
julia> macro var"let"(); @info "let"; end
@let (macro with 1 method)

julia> macro var"&"(); @info "&"; end
@& (macro with 1 method)

julia> @&
[ Info: &

julia> @let
[ Info: let

```

The nightly REPL wants uses of `@let` to tab-complete to `@var"let"`, but both do work.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [April 16, 2025, 8:20pm UTC](https://discourse.julialang.org/t/illegal-macro-names/128136/5 "2025-04-16T20:20:48Z")

</div>

That seems right. `macro (let)() end` throws a `ParseError` because the parser expected a `let` expression and won’t accept the `)`. `@eval` and `var""` indeed are ways to put keywords into macro names. It can also do that for _function_ names, but you’d need module qualification in calls to get around the parser insisting on keywords.

```julia
julia> @eval $(:for)() = 0
for (generic function with 1 method)

julia> Main.for()
0

julia> eval(:(for())) # or simply for() and press Enter twice
ERROR: ParseError:
# Error @ REPL[43]:1:13
eval(:(for()))
# └ ── invalid iteration spec: expected one of `=` `in` or `∈`

```

I hope you’re sticking to the macros because the keyword-resembling functions are cursed IMO.

---

<div class="post-metadata">

**Author:** ![digital\_carver](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/digital_carver/32/33818_2.png) [@digital\_carver](https://discourse.julialang.org/u/digital_carver)\
**Post date:** [April 17, 2025, 4:42pm UTC](https://discourse.julialang.org/t/illegal-macro-names/128136/6 "2025-04-17T16:42:27Z")

</div>

> [@MilesCranmer](#):
>
> The idea here being you could have something more Rust-like for [BorrowChecker.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/BorrowChecker):
> 
> ```julia
> @let x = [1, 2, 3]
> @let :mut y = [4, 5]
> 
> ```

Regardless of syntactic workarounds, I’d find it really unfortunate if our code was littered with two different kinds of `let` that mean different things. The `&` case is a lot more defensible and (IMO ) innocuous, but I’d say the errors are doing the right job in `let`’s case.

I think paying the cost of slight deviation from Rust syntax (calling it `@letrs` or `@bclet` or anything slightly different like that) is well worth it to not have a confusing `@let` that’s semantically quite different from Julia’s `let`.

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [April 17, 2025, 6:35pm UTC](https://discourse.julialang.org/t/illegal-macro-names/128136/7 "2025-04-17T18:35:13Z")

</div>

Good point! Ok I will avoid it.

At the moment, BorrowChecker.jl has `@own` for creating owned values and `@ref` for creating references.

Since I’d like to add a `@&` for declaring borrowed types I wondered if it would make sense to combine `@own` and `@ref` into a single macro that would change type based on whether you used `@&` or not. I’m not sure.

`@&` would be used like this:

```plaintext
function push!(x::@&(:mut, MyVector), i::@&(Int))
    #= ... =#
end

```

(it renders nicer in vscode)

Explanation: It is basically a way to declare the mutability of input references, something which Julia doesn’t have syntax for at the moment. The `!` is often used as an indication the first arg might be changed, but there’s no guarantee, and it doesn’t allow for multiple mutable arguments. Also doesn’t constrain anything in type space, whereas this does!
