Did you go with first class environments? If so defmacro builds a fexpr with an an extra eval in the dynamic environment plus a flag to tell the compiler it must be inlined (and that various other things should be reported as semantic errors).
You can get somewhat close to fexpr with lazy evaluation but it turns out that's not the same thing either. Taking the arguments apart and building different things out of them is a superset of stashing it somewhere to evaluate later.
(lambda (x) ...) ; evaluate the argument in the caller, bind it to the name x
(fexpr (x) ...) ; bind the argument to the name x
(normal (x) ...) ; make a thunk out of the argument and the environment
(macro (x) ...) ; bind the argument to x then do the force inline & extra eval on call
It's interesting that macros are the one with a bunch of extra work to do after the call whereas fexpr does the bare minimum of adding stuff to the lexical environment. Lambda maps eval across the arguments, normal maps make-thunk across the arguments.
You can get somewhat close to fexpr with lazy evaluation but it turns out that's not the same thing either. Taking the arguments apart and building different things out of them is a superset of stashing it somewhere to evaluate later.
(lambda (x) ...) ; evaluate the argument in the caller, bind it to the name x
(fexpr (x) ...) ; bind the argument to the name x
(normal (x) ...) ; make a thunk out of the argument and the environment
(macro (x) ...) ; bind the argument to x then do the force inline & extra eval on call
It's interesting that macros are the one with a bunch of extra work to do after the call whereas fexpr does the bare minimum of adding stuff to the lexical environment. Lambda maps eval across the arguments, normal maps make-thunk across the arguments.