I quickly build a small macro that does this (probably not in a very robust way) and wanted to give a small outline on how to approach such a problem For the solution look at the end.

When writing a macro a good first step is to write down what the transformation of the source code should actually be. In this case, as you noted, the desired functionality is already present for functions but also for `let`

blocks. So it is sufficient for the macro to transform:

`@imp(; a = 2, b = a^2, c = 3) `

into

```
let a = 2, b = a^2, c = 3
(; a = 2, b = a^2, c = 3)
end
```

So the next step is looking at the AST to figure out how to puzzle together the desired structure of `Expr`

s from the ones we recieve. For that we can use `dump`

(and `Base.remove_linenums!`

to strip away unnecessary nodes our macro won’t see but that `dump`

might pick up).

```
julia> dump(Base.remove_linenums!(quote
let a = 2, b = a^2, c = 3
(; a = 2, b = a^2, c = 3)
end
end); maxdepth=12)
Expr
head: Symbol block
args: Array{Any}((1,))
1: Expr
head: Symbol let
args: Array{Any}((2,))
1: Expr
....
```

To get the input of the macro, I recommend actually using a macro, e.g:

```
macro dump(expr)
return :(dump($(QuoteNode(expr))))
end
```

Then you get

```
julia> @dump(; a = 2, b = a^2, c = 3)
Expr
head: Symbol parameters
args: Array{Any}((3,))
1: Expr
head: Symbol kw
args: Array{Any}((2,))
1: Symbol a
2: Int64 2
....
```

With that it is not very hard to come up with the transformation. Here is your solution:

```
macro imp(expr)
expr.head == :parameters || error("I don't understand this: $(dump(expr;maxdepth=4))")
bindings = Expr(:block, (Expr(:(=), e.args[1], e.args[2]) for e in expr.args)...)
body = Expr(:tuple, expr)
return esc(Expr(:let, bindings, body))
end
```