I would like to write a macro that replaces one identifier with another, but it needs to respect lexical scope.
Here is an example illustrating what I mean:
defmodule Foo do
defmacro rename({i1, _, c1}, {_i2, _, _} = i2c, do: body) do
Macro.postwalk(body, fn x ->
case x do
{^i1, _, ^c1} -> i2c
w -> w
end
end)
end
def foo() do
quote do
rename x, y do
x = 42
z = fn x -> x + 2 end
x + 1
end
end
|> Macro.expand_once(__ENV__)
|> Macro.to_string()
|> IO.puts()
end
end
Foo.foo()
When I run this code it prints the following: (meaning: the reanme do ... end
block expands to the following)
y = 42
z = fn y -> y + 2 end
y + 1
The problem here is that the x
variable introduced on line 3 by the fn
form gets rewritten, even though that x
refers to a different variable than the x
on line 1. What I want is a definition for rename
that, when run on the same code, prints something like this:
y = 42
z = fn x -> x + 2 end
y + 1
Context
Above is just a toy problem meant to illustrate what I would like to try to do. I’m working on a bigger and much more complicated macro that might need to rewrite variables in arbitrary expressions, but I want to make sure that my macro respects lexical scoping.
Racket can do something like this with make-rename-transformer
, which creates an identifier macro that expands to another identifier. When you run this code:
#lang racket
(require syntax/parse syntax/parse/define
(for-syntax racket/match))
(define-syntax (replace stx)
(syntax-parse stx
[(_ (i1:id i2:id) body:expr)
#'(let-syntax ([i1 (make-rename-transformer (syntax i2))]) body)]))
(expand-once #'(let ([x 1] [y 2]) (replace (x y) (+ x (let ([x 1]) x)))))
you get this output:
#<syntax:ident_test.rkt:11:10
(let-values (((x) (quote 1)) ((y) (quote 2)))
(let-values ()
(let-values () (#%app + y
(let-values (((x) (quote 1)))
x)))))>
if it’s not immediately obvious, the variable x
has been swapped out for y
except inside of the inner let
block, which introduces a new variable x
.
I would like to see if I can do the same in Elixir. I know that this is a little unusual; I’m a researcher, so I do unusual stuff by definition.
Thanks in advance for any help.