Is another way of saying your source files might not do what they say! You're not selling it to me.
Is another way of saying your source files might not do what they say! You're not selling it to me.
The disconnect between what your source file "does" and what it "says" is present in everything above assembly.
Change implementation of `foo(...)` and suddenly dozens of source files (none of which is modified!) which make calls like `foo(x)`, `foo(y)` and `foo(y+1)` change what they "do", all this without changing a single line in these files.
x.y = foo(z, t)
reading as "there is a variable labelled x, with a defined type within this scope, that contains a struct member y with a statically-computable offset within x's known memory layout, that will receive a copy of the function foo (which is known at compile time), which is the result of calling foo with a copy of z and t, which are also values with compile-time known types and layouts", even if you as a human may have to look up foo to know what is going on, and the equivalent in Python, which I will break out into a separate paragraph because this is going to get long even though it will still be incomplete:"there is a reference x to an unknown type, which we will call one of several methods to resolve a field called y, where each of these methods may have been dynamically modified by previous code to not match their source implementation, which may either be a concrete value or a "descriptor", which will will have some method call performed on it (with full OO resolution rules on the method call, including the ability for the class structure to have been dynamically modified by code at run time and also all methods in the class structure to have been dynamically modified, and also the method can do anything a method can do including its own further dynamic modifications to arbitrary other structures, and also the result of running class and method decorators on everything in this hierarchy) with the result of calling foo, a values of unknown type that may be a function, method, or arbitrary values with a __call__ value (which itself may perform arbitrary dynamic operations in the process of returning a callable value), with copies of a reference to the values z and t of unknown type and capability, which will be filled in to foo's argument list in additional possibly complicated manners based on foo's definition and whether it uses args or kwargs, and also all of this code including the very statement under question may not exist in source at all because it may have been 100% dynamically constructed at run time itself."
And I probably missed some things in the Python too, but even so, Python already has numerous "and actually literally arbitrary amounts of code could be running here", and can readily introduce more.
All languages have the issues where you might have to resolve a function call as a human to know what is is doing, but in some languages, you're nearly done after that (C, Go) and in some languages your journey is just beginning (C++, Python).
The number of other special mechanisms in Lisp is much lower than in Python or C++.
Obviously macros are powerful which is another way to say "footgun". Also it makes the comparison a bit unfair - an out-of-the-box Lisp usually already has many macros which you have to know. They provide some of the stuff which needs to be hard coded into the compilers in languages like C++ and Python.
Agreed in the general sense, which is why I chose Python for my example, for clearer contrast.
I would say there's still a qualitative difference between languages where there is some well-defined, concrete procedure where you can take a line of source code and resolve exactly what it is doing, and a language where said resolution of what it is doing involves arbitrary code execution between the source code and what the code is actually doing. Python is frankly insane in some ways, but you really only need one place where arbitrary code is being invoked, and I mean this in the nicest of ways but Lisp programmers are fully capable of driving a truck through even that one particular hole.
What you probably mean there is you want some abbreviation or specification for what the code does without having to expand it. It's not clear to me how this could work with all the things (like preprocessors or code generators) that macros can replace. Maybe you want a standardized API where the macro, in addition to computing the expansion, also delivers some sort of specification of what the expansion is doing? Or maybe you just are asking that macros be documented, like any built-in language feature?
Questions like this, I find, are best addressed by drilling down and asking for specifics about what you need.
A lot of javascript hot reloading implementations rely on using Babel to do that as part of a build step so they output the wrapped code and your app loads that instead (and it's not that dissimilar to compilers having a debug mode where they output very different code).
I would certainly side-eye you for using the relevant common lisp hooks in production without a very convincing reason, but for many development time tasks it's basically the civilised version of widely used techniques.