7 ms·
Some really interesting things may happen when you add arbitrarily powerful macros to a bondage and discipline non-Turing-complete language.
by sklogic 11y ago
Some really interesting things may happen when you add arbitrarily powerful macros to a bondage and discipline non-Turing-complete language.
- randcraw 11y agoOne of them is that, each time you read a new program, you have to learn a new language. Probably no language benefitted / suffered from this more than Lisp.
- kazinator 11y agoAny signficant project in any language has a "dictionary" of custom identifiers, whose vocabulary you have to learn if you want to become a maintainer. The thousand functions in a big C program are just as much a language, as some Lisp macros. The functions all do something. That something isn't "transform the code", but that doesn't matter; if you're looking at the function call and don't know what it does, you're just as lost. Basically, if you're reading Lisp, you go from "outside in" and just assume that everything you see whose definition you are not familiar with is a macro. Well-behaved code follows certain unwritten conventions. For instance, it avoids creating confusion by exhibiting multiple completely independent uses of the same symbols in the same scope. So for instance if we have (frobozz a b) wrapped in a lexical scope where we have (let (a b) ...) in effect, this well-behavedness principle tells us that the a and b symbols in (frobozz a b) refer to these variables. So, we can probably lay aside our suspicion that, for instance, frobozz is redefining some unrelated a in terms of some unrelated b. However, frobozz might be a macro; so we can't cast aside our suspicion that either a or b has its value clobbered. (Same in Pascal or C++, with ordinary functions: frobozz(a, b) could take VAR parameters or references, respectively).
- eudox 11y agoThis isn't really the case. Some libraries are all macros, some libraries have no macros, but most have a few macros that quickly fall into general patterns: some macros for setting up and tearing down context safely (with-thing macros), others for iterating some data structure without revealing its internals (do-thing macros), generally simple stuff.
- sklogic 11y ago> One of them is that, each time you read a new program, you have to learn a new language Which is not bad at all. You still have to do it with the programs written in the same language built with different libraries and targeting the different problem domains. In the latter case the language is obscuring the essence of the code. And if you're using a well designed DSL, and you're familiar with the problem domain, it will be readable naturally, just like a pseudocode.
- lmm 11y agoA lot of domain languages need infix operators though. And even if you understand the domain, it's hard to read and refactor code in the presence of unrestricted macros, whereas in a language where such DSLs are implemented via the type system the tooling will understand that.
- Jtsummers 11y agoWhy do they need infix operators? Most programmers already use prefix notation with function calls. Infix is primarily restricted to mathematical and logical operations, but the prefix (lisp style, taking an arbitrary number of arguments) is pretty clear when written neatly: (and cond1 cond2 cond3) (NB: multiple lines like this would really only be used for a lot of conditions or longer expressions.) We already do something similar with our algol-like languages: if((cond1 || cond2) && (cond3 || cond4) && (cond5 || cond6) && (cond7 || cond8)) In lisp, CL at least, would look like: (if (and (or cond1 cond2) (or cond3 cond4) (or cond5 cond6) (or cond7 cond8))
- lmm 11y agoBut at that point it's no longer the language of the domain. If you're going to support writing mathematics that looks like the mathematics that mathematicians write then you need infix operators, because mathematics is written with infix operators. Similarly for many other domains.