3 ms·
As I see them. well-designed macros are just functions whose input and output is an AST of code. What is the difference then? Macros should not be abused, but
by nextos 7y ago
As I see them. well-designed macros are just functions whose input and output is an AST of code. What is the difference then?
Macros should not be abused, but having macros is a huge leap forward in terms of expressiveness.
- chongli 7y agoWhat is the difference then? Macros don't exist at runtime, functions do. You can't pass a macro to a function nor return one. You can't store a list of macros to apply to a value. Macros are nothing more than user-programmable syntactic sugar. In a language like Haskell, you can do with functions most things that would only exist as macros in Lisp.
- gmfawcett 7y agoYet even Haskell has Template Haskell, and it is widely used (with the convenience syntax of Lens as an example). Haskellers seem to have conceded that, in spite of their language's power, multi-stage programming -- where higher-staged functions don't exist at runtime! -- has an established place in the ecosystem.
- ddragon 7y agoNot existing at runtime seems more like a performance consideration. The macro could very well had just deferred evaluation of it's arguments, so you could manipulate the AST during runtime and return the code for immediate eval, and it would work provide the same syntactic sugar but with a runtime function (and it's probably how it works when an interpreter evaluate the macros instead of a compiler). But by moving that transformation to compile time, you get not only the power to create syntactic sugar for the programmer but also to better control what the compiler will generate (and cache it, so you'll be free from the asymptotic complexity of the runtime control flow). That's great for both AoT compilation, for which you can effectively remove parts of the algorithm that can be pre-computed from the output program, and JIT for which runtime and compile time can interact to more intelligently distribute the workload.
- chongli 7y agojust deferred evaluation of it's arguments You get that for free with a lazy language. You can write any sort of control flow mechanism to your heart's content and it's just a function in Haskell. People have written all manner of fancy coroutine systems, continuations, etc, without the need for macros. moving that transformation to compile time Haskell, gives you a great deal of control over this. Some of the best libraries make use of this functionality for things like stream fusion. Additionally, Haskell can lift constants to the top level so they're only evaluated once, on demand. This gives you free memoization without increasing code size (as you would if you precomputed a large constant).
- ddragon 7y agoI wasn't trying to argue that this was the only or even the best way of doing it, just why it's advantageous that you separate runtime and compile time functions, especially in languages with eager evaluation. The advantage of macros is just that they are simple to understand and use (for most you basically just have to understand tree data types, or just cons, especially for CL style macros), to implement (since every (?) language already have an AST representation, and particularly powerful in Lisp for obvious reasons) and the language doesn't even really have to support them in their normal syntax (fully separating runtime programming syntax and compile time programming syntax if they want). And for that they give a lot of power by allowing easy language extensions, syntactic sugar and powerful optimizations.