So it's long been theorized that there could exist a form of function that would splice its result into the feed of execution. And now we have it:
weirdness: inliner [x [block!] :refinement] [
either refinement [
'reverse
][
spread compose [append (x) first]
]
]
>> data: [a b c]
>> weirdness data [1 2 3]
== [a b c 1] ; acts like you wrote `append [a b c] first [1 2 3]`
>> data
== [a b c]
>> weirdness:refinement data [1 2 3]
== [1 2 3] ; acts like you wrote `reverse [a b c] [1 2 3]`
>> data
== [c b a]
So you return either an element or a SPLICE! of what you want injected at the callsite. (Or a VOID to have same effect as an empty splice.) If you want things to be bound to things that the INLINER knew about (e.g. the argument X in the case above) then you have to bind it before you give it back as a splice.
NOTE: Here Be Dragons! 
The function interface for what the inliner produced above looks like it only takes one argument, but it winds up being effectively variadic. Here you see it deciding to take one or two.
Note that if you put an infix operator at the beginning of a macro splice, it will not be able to see its left. So if you want an inliner to see the left hand side parameter, the inliner itself has to be infix. It will see the infix parameter, but won't "consume" it:
add1020: infix inliner [] [
return [+ 1020]
]
>> 304 add1020
== 1324
As a general reminder of why you can't decide infix "after the fact"... the concept of a single instruction "step" is one that has a finishing point. If all functions reserved the right to be infix, then that would mean a EVAL of a BLOCK! couldn't be separated into individual evaluation steps...each function would run glued to the last. It would be possible to avoid this if COMMA!s between expressions were enforced so you called out where every expression ended, but we obviously do not want that!