lyn@altdorf.ai.mit.EDU (Franklyn Turbak) (03/05/91)
This is a long message on why macros impair readability. Read the abstract for a summary. I'm interested in hearing feedback, alternate models, and lots of anecdotes and examples. - Lyn - --------------------------------------------------------------------------- WHY MACROS IMPAIR READABILITY Franklyn Turbak March 4, 1991 ABSTRACT -------- Arguments about program readability are often based on many implicit assumptions about the definitions of "reader" and "readability". Such arguments would be more compelling if (1) these definitions were made explicit and (2) the arguments were based on technical considerations in terms of concrete examples. Using an interpretation where "readable" means "supporting local reasoning about program fragments", I discuss four reasons why macros impair readability in Scheme: 1. Applicative Order Evaluation 2. Static Scope 3. Procedures as First-Class Objects 4. Debugging I conclude with an entreaty for alternate analyses and anecdotes of how macros help/hinder code readability. WHAT IS READABILITY? -------------------- In the recent debate on the Scheme mailing list about the advantages and disadvantages of macros, a number of arguments were made about "readability" of code containing macros. But the notion of readability is rather slippery. First of all, "readable" code admits many possible interpretations, including: * Concise - free of baggage not important to the ideas being expressed. * Understandable - matched with the reader's mental structures. * Expressive - accurately conveys the writer's mental structures. * Modifiable - structured to permit extensions and variations. * Well-documented - contains helpful descriptions, comments, names. * Verifiable - aids in the proof (by people or machines) of properties of the described process. * Recallable - easy to remember or rederive. * Teachable - explainable to someone else. Second, all these interpretations depend a heck of a lot on who the reader is. Readers vary widely in background, programming skill, and purpose for reading the code. A "most readable" style is a fiction; what is clear and concise to some readers may be inscrutable to others. Many people can (and do) agree on matters of programming style; nevertheless, different styles are tuned to different kinds of readers. For example: * A WHILE macro may be a boon to an imperative thinker and anathema to a functional thinker. * Extensive documentation that aids some readers gets in the way of other readers who want to see more of the code in single editor buffer. * Conventions like thunking args to delay evaluation, implementing message-passing objects as procedures, or using continuation-passing style to achieve nonstandard control flow are clear as day to those facile with these techniques, but (1) pose difficulties to those not familiar with these devices and (2) are candidates for abstraction by those who believe such details obscure the essence of the code. * An interpreter using concrete rather than abstract syntax is well-suited for class presentation because it is shorter and is more likely to fit in its entirety on a blackboard. On the other hand, a version with abstract syntax may be better suited to lab study, where the code readers may want to implement an alternate syntax. * For a person who simply wants to use a given program, a description of its interface & behavior and clearly marked entry points are crucial. For someone attempting to extend a program, hierarchical structure and accessibility of "hooks" are important. Clear structuring of data and control flow are essential for readers who want to understand particular algorithms. TOWARDS A MORE OBJECTIVE ANALYSIS OF READABILITY ------------------------------------------------ Given the above, it's easy to see how discussions about readability can easily degenerate into religious squabbles. If everyone assumes his/her own interpretation of "readable" and "reader", then people aren't really debating the same issue. One way to improve the situation is for discussants to be more explicit about their assumptions. Another improvement would be the use of specific examples rather than vague generalities. "This particular macro improves/impairs readbility because ..." is much more convincing than nebulous claims about factors enhancing or detracting from readabilty. But even more desirable would be arguments with a more formal or objective basis for comparison. In light of this goal, I consider four linguistic issues to illustrate why Scheme code using macros can less be readable than Scheme code without macros. Here, I use the term "readable" to mean "easy to reason about locally", where locally refers to the fact that certain conclusions can be made about a code fragment without knowing the full context in which it occurs. I also assume that the program being read is a large one, so that there is a nontrivial overhead to obtaining global information, such as finding top-level definitions. Local reasoning is particularly valuable in such situations. Finally, I assume that the reader desires a detailed understanding of the code, not just a feel for it's high-level structure. 1. APPLICATIVE ORDER EVALUATION ------------------------------- A common use of macros is to simulate normal order evaluation of arguments within Scheme's applicative framework. For example, it is possible to implement lazy pairs by the desugarings: (LAZY-CONS <exp1> <exp2>) => (CONS (LAMBDA () <exp1>) (LAMBDA () <exp2>)) (LAZY-CAR <exp>) => ((CAR <exp>)) (LAZY-CDR <exp>) => ((CDR <exp>)) (Both LAZY-CAR and LAZY-CDR could be procedures, but LAZY-CONS must be a macro.) Although using macros in this way can reduce the clutter of thunks, it makes it more difficult to reason about the evaluation of expressions that appear in the argument positions of a procedure/macro call. In macro-less Scheme, for example, the expression (unknown (letrec ((loop (lambda () (loop)))) (loop))) must be nonterminating regardless of the meaning of UNKNOWN because all arguments must be evaluated before the procedure is called. But in the presence of macros, an argument expression may be evaluated zero times, so the above could return a value. Macros require the reader to use more global knowledge to understand this fragment. Similarly, with macros an argument expression might be evaluated more than once. This can wreak havoc in the presence of side effects. In the expression (let ((x 0)) (unknown (begin (set! x (+ x 1)) 17))) X is incremented only once in macro-less Scheme, but might be incremented any number of times depending on the definition of UNKNOWN, if it were a macro. Granted, the above examples are contrived, and it is generally considered bad policy to have side-effects in argument positions. Nevertheless, the same problems can crop up in much more natural situations. The point is that local reasoning valid in a purely applicative-order language is no longer necessarily valid in the presence of macros. Note that this problem is ameliorated by Aubrey Jaffer's suggestion of distinguishing macro names from procedure names (or macro calls from procedure calls). In that case, the usual Scheme reasoning can be used in the vast majority of the cases (procedure calls), but the potentially troublesome cases are syntactically flagged. 2. STATIC SCOPING ----------------- The kind of lexical reasoning enabled by Scheme's static scope can be invalidated in the presence of macros. Consider the expression: (let ((return (lambda (n) (* 2 n)))) (block (+ 100 (return 3)))) If BLOCK were a procedure, then the RETURN that appears within its argument would have to refer to the multiply-by-two procedure, and the meaning of the expression would be the same as that of (block 106) Of course, we couldn't say more about the meaning of the whole expression until we also knew more about the behavior of the BLOCK procedure. But it would still be possible to make a firm conclusion about the value of BLOCK's argument without any more global information. In the presence of macros, all bets are off, since BLOCK might be a macro that (intentionally) binds the name RETURN within its scope. E.g., a desugaring for BLOCK might be: (BLOCK <exp>) => (CALL-WITH-CURRENT-CONTINUATION (LAMBDA (RETURN) <exp>)) (Note this naming issue is different than the "accidental name capture" problem associated with faulty macro implementations. Here the macro writer really wants the name RETURN to be captured.) The BLOCK macro might even treat its entire argument as text: (BLOCK <exp>) => (QUOTE <exp>) Here, the name RETURN is just a symbol, and not a variable reference after all. Here we have a situation where the introduction of macros has the potential of complicating the scope rules that programmers use to perform local reasoning about their programs. Counterarguments to this point are: (1) Such examples are extremely rare. (2) Macros that intentionally bind names should not be allowed. But the fact remains that in the presence of macros, the *possibility* that names might not have their normal lexical interpretation must at least be considered by the reader. Again, Jaffer's proposal to syntactically distinguish macros from procedures would clearly delineate those regions of code where the usual reasoning about scoping might not apply. 3. PROCEDURES AS FIRST-CLASS OBJECTS ------------------------------------ Scheme encourages programmers to exploit the first-class nature of procedures. Thus, procedures are commonly named, passed as arguments, returned as results, and stored in data structures. A disadvantage of macros is that they cannot be treated in this way. For example, AND is commonly treated as a macro with the desugaring: (AND <exp1> <exp2>) => (IF <exp1> <exp2> #f) There are situations where it is desirable to pass AND as an argument: (define (accumulate combiner null-value lst) (if (null? lst) null-value (combiner (car lst) (accumulate combiner null-value (cdr lst))))) (define (all-true? lst) (accumulate and #t lst)) Unfortunately, this doesn't work; the AND must be encapsulated into a procedure before it can be passed: (define (all-true? lst) (accumulate (lambda (x y) (and x y)) #t lst)) This problem is not so much one of macros destroying local reasoning properties but rather one of verbosity and inconsistency. Still, without knowing the definiton of AND, a reader modifying code cannot safely replace (LAMBDA (X Y) (AND X Y)) by AND. Such a local modification would be valid in macro-less Scheme. Yet again, a convention flagging macro names would alleviate the situation. 4. DEBUGGING ------------ Though macros may aid in making source code more concise, the macro-expanded code can often be rather unwieldy. The expanded code is normally hidden from the reader, but often rears its ugly head during debugging. Consider an example from the Mini-FX programming language used in the graduate programming languages course at MIT. (Mini-FX is a simplified version of Dave Gifford's FX language implemented as a macro package on top of Scheme). Mini-FX supports a powerful pattern matching construct called MATCH. Below is an example where MATCH is used in the definition of a list reversal procedure: (define (reverse lst) (match lst ('() '()) (`(,first ,@rest) (append (reverse rest) (list first))))) My goal here isn't to describe the semantics of MATCH, but simply to show that it can lead to extremely complex macro expansions. The above definition expands into: (define (reverse lst) (if (equal? lst '()) '() (let ((#fail-25 (lambda () (error (string-append "MINI-FX RUNTIME ERROR (This error should be caught by the typechecker!):\n" "MATCH -- no pattern matched") lst)))) (list->sexp~ lst (lambda #success-arg-27 (if (not (= (*minifx-length* #success-arg-27) 1)) (*minifx-success-number-of-args-mismatch* '((cons~ first rest)) #success-arg-27) (apply (lambda (#temp-26) (cons~ #temp-26 (lambda #success-arg-28 (if (not (= (*minifx-length* #success-arg-28) 2)) (*minifx-success-number-of-args-mismatch* '(first rest) #success-arg-28) (apply (lambda (first rest) (append (reverse rest) (list first))) #success-arg-28))) #fail-25)) #success-arg-27))) #fail-25)))) A user who makes an error within a MATCH clause will be thrown into a debugger that has access to the verbose expanded code but not the concise unexpanded code. Here we have yet another kind of code reader facing locality difficulties of a different sort. In this case the expressive advantages offered by syntactic abstraction have disappeared, and the reader is left with the job of matching up the expanded code with the appropriate section of the source code. Had procedural abstraction been used instead, this matching up process would be greatly simplified. This problem seems less intrinsic than the others because it seems possible to design a "smart" debugger that would aid in the inverse of macro expansion (= macro contraction?). Nevertheless, in Scheme systems I have seen, the above problem is very real one for the reader-as-debugger. Note that this problem is due to the very nature of macros; Jaffer's syntactic distinction scheme will not help here. DISCUSSION ---------- Please note that I am *not* claiming that Scheme without macros is inherently readable. Such a claim is absurd, because it is possible to write bad programs in any language. And as Mark Friedman and others have pointed out, there are many reasons why macro-less Scheme can be hard to read. I also do not claim that introduction of macros into a program always makes it less readable. There are many situations where a judicious use of macros makes code more understandable by abstracting over the particular mechanism that implements a behavior. (Unfortunately, macros are notoriously hard to write well; macrology is quite a black art.) What I *am* claiming is that macros introduce a new set of *potential* reasoning difficulties in addition to the ones that are already present in macro-less code. Whether these difficulties *actually* impair programmers' reasoning in practice is an empirical issue. Many of the above examples are simple and contrived. I'd like to hear about specific cases where people think these (or other) issues were at play in macros hindering their reasoning. Note that in three of the four points raised above, Jaffer's syntactic distinction idea seemed well-motivated. This conclusion is based on my particular assumptions. Of course, there are other assumptions and models under which macros improve reasoning and macro/procedure syntactic distinction unduly complicate programs. I entreat people to make such assumptions and models explicit in their readability arguments, and to make liberal use of examples in illustrating their viewpoints.
markf@zurich.ai.mit.edu (Mark Friedman) (03/07/91)
First let me say that I applaud Franklyn's attempt to unravel and explicate the issues involved Scheme code readability. I also marvel at and envy the clarity of his writing. OK, now that I have buttered him up ... I think that his arguments are sound but his premise is faulty. In particular, after presenting many reasonable interpretations of the term "readable" he chooses the least useful. He uses the term to mean "easy to reason about locally". The problem is that for large (and well abstracted) programs local reasoning gets you very little information. For example, consider the following procedure: (define (foo bar) (mumble ((grumble bar) baz fumble) zaz) grok (((bar)))) Would it really help to know that grumble is a macro? Unless you know what grumble really does (or at least what it is supposed to do) the issue of whether it is a macro or a procedure is fairly moot. And in the process of discovering what grumble actually does you will find out whether it is macro or a procedure. Now admittedly, macros are, on the whole, harder to figure out than procedures, but that is not a issue of local readability. A sufficiently complex macro (or procedure) must be well documented. Higher level macro decription languages like extend-sytax help a lot here also. Your specific arguments against macros are well taken, but they seem to imply a certain naive view (not that I think that Franklyn is naive in any way :-) of the Scheme evaluation model. Scheme is not the lambda calculus and it's not referentially transparent. Macros do add extra comlexity, but I don't feel that they qualitatively change the rules of the game. More importantly, they give us another powerful abstraction mechanism. -Mark -- Mark Friedman MIT Artificial Intelligence Lab 545 Technology Sq. Cambridge, Ma. 02139 markf@zurich.ai.mit.edu
lyn@altdorf.ai.mit.EDU (Franklyn Turbak) (03/08/91)
This a response to Mark Friedman's evaluation of my analysis of macro readability: > I think that his arguments are sound but his premise is faulty. > > In particular, after presenting many reasonable interpretations of the > term "readable" he chooses the least useful. He uses the term to mean > "easy to reason about locally". The problem is that for large (and > well abstracted) programs local reasoning gets you very little > information. I agree wholeheartedly; my definition of "readability" *is* weak and not very useful. However, it's the only one I could think of at the time of writing that allowed me to raise the set of points I wanted to raise: that macros complicate reasoning about order of evaluation, static scope, first-classness, and debugging. I'd rather have a much stronger notion of readability that more people find in tune with their intuitions. From the discussions so far, there's a lot of people who *do* feel that macros can make programs hard to read. My goal is to understand better *why* they feel this way. Over the years, I personally have come to view the macro as a kind of powerful drug; its judicious use can improve programs, but its abuse leads to programs that are hard to read. But why do I feel this way? I assume it's because I've gotten "burnt" too many times when trying to read code that contains lots of macros. I wish I had written down or could remember all these situations --- then I'd have at chance at analyzing exactly what the difficulties were and how my feelings about macros developed. Maybe the macros tended to introduce gratuitous syntax, Maybe they were often written or documented poorly. Maybe I just didn`t have a good enough model of macros to understand what they meant or why they were being used. Unfortunately, I don`t remember these situations, and am left with the task of rationalizing my feelings. My analysis was only a first attempt at this task. It is not convincing in many ways and needs to be improved. I'm hoping that further discussion on this mailing list will elucidate the fundamental issues involved in the effects of macros on readability. > For example, consider the following procedure: > (define (foo bar) > (mumble ((grumble bar) > baz > fumble) > zaz) > grok > (((bar)))) > Would it really help to know that grumble is a macro? Unless you know > what grumble really does (or at least what it is supposed to do) the > issue of whether it is a macro or a procedure is fairly moot. And in > the process of discovering what grumble actually does you will find > out whether it is macro or a procedure. > Now admittedly, macros are, on the whole, harder to figure out than > procedures, but that is not a issue of local readability. A > sufficiently complex macro (or procedure) must be well documented. > Higher level macro decription languages like extend-sytax help a lot > here also. Yes, the local reasoning criterion is weak. The key to a more usable definition involves formalizing your intuition that macros "are harder to figure out than procedures". What makes them harder to figure out, and how does this impact on readability? I am working on an improved model of "readability" to better handle your objection. The basic idea is that macros are harder than procedures for the reader to model behaviorally because of the various funky things that can be done in complex desugarings. So understanding (and remembering for later use) macro definitions and calls requires more cognitive overheard than procedure definitions and calls of similar syntactic complexity. Maybe more limited versions of syntactic abstraction (like EXTEND-SYNTAX) are sufficiently constrained to be more amenable to behavioral modelling than general macro facilities. Obviously, there are a lot of details to be worked out here. Does anyone else who believes that macros impair readability have any ideas along these lines? > Your specific arguments against macros are well taken, but they seem > to imply a certain naive view (not that I think that Franklyn is naive > in any way :-) of the Scheme evaluation model. Scheme > is not the lambda calculus and it's not referentially transparent. > Macros do add extra comlexity, but I don't feel that they > qualitatively change the rules of the game. More importantly, they > give us another powerful abstraction mechanism. You say that macros are just another abstraction mechanism that do not qualitatively change the rules of the game. But then why is it that syntactic abstraction and procedural abstraction evoke such different responses with regard to readability. I rarely (if ever) hear programmers complaining that code was hard to read because it contained procedures, but *do* hear programmers making these kinds of claims about macros. - Lyn -
markf@zurich.ai.mit.edu (Mark Friedman) (03/08/91)
In article <9103071300.aa28959@mc.lcs.mit.edu> lyn@altdorf.ai.mit.EDU (Franklyn Turbak) writes: This a response to Mark Friedman's evaluation of my analysis of macro readability: I agree wholeheartedly; No no Alphonse, I agree with you wholeheartedly :-) You say that macros are just another abstraction mechanism that do not qualitatively change the rules of the game. What I meant was not that macros are JUST another abstraction mechanism, but that there are other things in Scheme which complicate the rules (e.g. side effects and call-with-current-continuation). We accept them (and sometimes scorn them) because of their power, just as we should accept (and sometimes scorn) macros. But then why is it that syntactic abstraction and procedural abstraction evoke such different responses with regard to readability. I rarely (if ever) hear programmers complaining that code was hard to read because it contained procedures, but *do* hear programmers making these kinds of claims about macros. I think that Scheme programmers are more aware of the pitfalls of things like side effects and call-with-current-continuation than they are of macros. To that end, this whole discussion (especially your part) is incredibly useful. If you look at code written in Scheme (or more likely Common Lisp) with a "Fortran style" it is often very difficult to read. The combination of procedural abstraction with side effects to global variables can be deadly. A former Fortraner could (weakly) argue that the problem is the modularization into procedures. The code would probably be clearer is it were clumped together because we sort of expect that separate procedures act upon separate local state. Of course a Schemer would (strongly) argue for getting rid of the global variables. -Mark -- Mark Friedman MIT Artificial Intelligence Lab 545 Technology Sq. Cambridge, Ma. 02139 markf@zurich.ai.mit.edu
kend@data.UUCP (Ken Dickey) (03/09/91)
In comp.lang.scheme you write: >... I rarely (if ever) hear >programmers complaining that code was hard to read because it >contained procedures, but *do* hear programmers making these kinds >of claims about macros. >- Lyn - ...Probably because more people are given training in what is stylistically appropriate. This is expressed as "the learning curve is steep". Once passed the curve we understand procedures and don't have a problem reasoning about them (much). I feel that the same is true of syntactic transformations. Many (most?) people have not gone through the learning curve and write "half-baked" macro code. This is exacerbated when low-level macro interfaces are used, as the "rewrite rules" are not textually apparent. IMHO this precisely parallels the case of going from a "high-level" language to assembler--it takes a person knowledgeable in representation and call pattern usage to do the appropriate thing. The low-level is harder to understand, harder to debug, etc. -Ken Dickey kend@data.uucp
markf@zurich.ai.mit.edu (Mark Friedman) (03/09/91)
You know that something's wierd when you reply to your own response. I realize that I have boxed myself into a corner with my "macros DON'T impair readablity" statements. I will now try to wrestle myself out of the corner with a new statement: Macros themselves are unreadable. I think that this is more the crux of the matter than Jaffer's complaints about not knowing when something is a macro or Turbak's complaints about the dangers of using macros. Consider a procedure like: (define (foo bar) (let ((baz (grumble bar))) (mumble (stumble baz) (fumble baz)))) We like to say something like: First set baz to grumble of bar then compute stumble of baz and fumble of baz and then apply mumble to those. Now consider: (define-macro (foo bar) (let ((baz (grumble bar))) (mumble (stumble baz) (fumble baz)))) Now the the same description applies, but it is not a semantically meaningful description of what foo does. It is a description of the construction of something which does foo. It is the wrong (for us) level of description. What's more, the more we modularly decompose or macro definitions the harder things get. The program fragments get less meaningful and the combining process gets more complex. This, of course, is not susprising. After all macros don't specify (run time) processes, they specify program constructions which specify processes. Procedures, however, do describe processes - or do they? Well at one level, yes they do. You plug the definition of the procedure into the Scheme evaluation rules and a process ensues. But an interesting question - and the one that is relevant to the readability issue - is: Does a Scheme procedure definition describe a process that we can understand. That is, does our idea of a process match Scheme's idea of a process. I think that for many of us the answer is no, and the reason is similar to the reason why macros are hard to understand. Consider the following program snippet: (define (tough foo) (((((foo 10) 'bar) 12.2) 'wow) 0)) (define (foo bar) (foo2 (whoknows bar))) (define (foo2 bar) (lambda (x) (foo3 (mumble x) (rumble bar)))) (define (foo3 bar baz) (lambda (x) (lambda (y) (stumble x y (lambda (foo (zaz bar))) (baz))))) ... Many people find programs like these hard to understand. A large part of the reason is that they construct and combine procedure fragments (i.e. lambda expressions). These procedure fragments are similar to the program fragments created and combined by macros. Perhaps most importantly, the order of execution of these fragments is not immediately apparent. They may get stored for awhile or passed around for awhile before they are applied. This again is similar to the situation with macro definitions where the program fragments may be arbitrarily combined. The question still remains however of why these things are hard to understand. After all the Scheme evaluation rules are fairly simple. I haven't even used side effects or call-with-current-continuation or even an IF in the above examples. I don't really have an answer for this but I suspect that many of us have an essentially serial, imperative, limited stack (and heap) mind set that doesn't match very well with the evaluation of procedures like the ones given and complex macro constructions. -Mark -- Mark Friedman MIT Artificial Intelligence Lab 545 Technology Sq. Cambridge, Ma. 02139 markf@zurich.ai.mit.edu
jeff@aiai.ed.ac.uk (Jeff Dalton) (03/10/91)
The intention is to add macros, and its just a matter of unfortunate timing that they're not there already. That it is unfortunate is shown by (among other things) the recent messages arguing against macros. If Scheme already had macros, we'd be able to spend our time discussing how to write good, readable macros instead. Anyone who doesn't like macros or thinks they're unreadable doesn't have to use them. They'll have a choice. On the other hand, if they're not in the language those of us who want to use them can't. No choice, unless we want to write non-portable code. In article <9103041928.aa21342@mc.lcs.mit.edu> lyn@altdorf.ai.mit.EDU (Franklyn Turbak) writes: > Given the above, it's easy to see how discussions about readability >can easily degenerate into religious squabbles. If everyone assumes >his/her own interpretation of "readable" and "reader", then people >aren't really debating the same issue. I disagree almost completely that that is the problem. Of course, some people do have strange notions of what "readable" means. For example, you seem to think it should mean "easy to reason about locally". I think that in part you are confusing the question of the definition of readability with that of what counts as a good reason for claiming something is readable. Readability has an inherant subjective component, but it is not entirely subjective. In particular, if there's a question about whether something is readable, there can be good reasons for saying it is or is not. It is not just a matter of saying "I find it easy to read, so it's readable". "Easy to reason about locally" would count as a good reason. That doesn't mean we should consider it a _definition_ of readability, however. > I also do not claim that introduction of macros into a program >always makes it less readable. There are many situations where a >judicious use of macros makes code more understandable by abstracting >over the particular mechanism that implements a behavior. Yes, but you want to create the impression that on the whole macros make things worse as far as readability is concerned. And note that here you're going back to what we might call the naive notion of readability -- or are you saying that macros sometimes make it easier to reason locally? >(Unfortunately, macros are notoriously hard to write well; macrology >is quite a black art.) Some macros are hard to write well, others are not. > Note that in three of the four points raised above, Jaffer's >syntactic distinction idea seemed well-motivated. This conclusion is >based on my particular assumptions. Of course, there are other >assumptions and models under which macros improve reasoning and >macro/procedure syntactic distinction unduly complicate programs. I >entreat people to make such assumptions and models explicit in their >readability arguments, and to make liberal use of examples in illustrating >their viewpoints. That's certainly a way to make it as hard as possible for someone to argue that macros are readable. I hope that we don't have to make such arguments at all, and that macros will be added to Scheme regardless. N.B. I agree that macros introduce new problems for local reasoning and with the parts of your article devoted to that point. -- jd
jeff@aiai.ed.ac.uk (Jeff Dalton) (03/10/91)
In article <9103071300.aa28959@mc.lcs.mit.edu> lyn@altdorf.ai.mit.EDU (Franklyn Turbak) writes: >I agree wholeheartedly; my definition of "readability" *is* weak and not >very useful. However, it's the only one I could think of at the time >of writing that allowed me to raise the set of points I wanted to raise: >that macros complicate reasoning about order of evaluation, static scope, >first-classness, and debugging. There was a much easier solution, namely to raise those points without trying to tie them to the definition of readability. >I'd rather have a much stronger notion of readability that more people >find in tune with their intuitions. From the discussions so far, >there's a lot of people who *do* feel that macros can make programs >hard to read. My goal is to understand better *why* they feel this >way. I'd agree with that. But one reason is that macros are often written as fairly complex list structure manipulations. Backquote helps some, extend-syntax much more. There are a number of simple reasons like this, that don't require a deep analysis. >I am working on an improved model of "readability" to better handle >your objection. The basic idea is that macros are harder than >procedures for the reader to model behaviorally because of the various >funky things that can be done in complex desugarings. N.B. _can_ be done. The complaints ought to be about the funky things. Note too that a desugaring can be complex without the macro being hard to understand if it implements a well-defined, easy to manage abstraction. For example, I can understand list comprehensons in Haskell more easily than I can understand the desugaring.