Hacker News new | past | comments | ask | show | jobs | submit login

Anaphora have been controversial in the common lisp community as long as I have been involved. I imagine the same is true of the scheme community.



Yeah, anaphoric macros are a neat trick, a novelty. They're rarely useful in practice. Additionally, any time one would be useful in practice, you turn your anaphoric construct into a binding construct, and suddenly you have hygiene. To lift an example from @davexunit up-thread:

    (aif (foo)
      (bar it))
becomes instead

    (let-if (it (foo))
      (bar it))
A minuscule amount of extra typing to be explicit, and now you not only have hygiene, but you can properly nest such forms and even generalize the form to multiple bindings:

    (let-and ((x (foo))
              (y (bar)))
      (baz x y))


Anaphoric macros are used frequently in the HN source code, so "rarely useful in practice" strikes me as incorrect on both counts. Used well, they make code both shorter and more readable. That's a win-win. The arguments against them, by contrast, seem largely theoretical. Win-wins, a.k.a. having your cake and eating it too, are rare in programming, so those of us who think this way will choose that over theoretical purity any day.

These arguments boil down to little more than personal preference and, above all, habit. Anaphoric macros are the kind of thing you like if you like that kind of thing. http://quoteinvestigator.com/2015/09/09/like-sort/


To clarify, I mean that the percentage of macros which are usefully anaphoric is small, not that any given anaphoric macro isn't useful in practice. A macro like `aif` is useful quite often in practice, but there aren't very many macros like `aif`.

You're right that it's a personal preference. My own preference is to always be explicit (which also explains my relative distaste for Perl...). With my students, I'm constantly emphasizing that "programs must be written for people to read, and only incidentally for machines to execute"[1]. I feel as though explicity goes hand-in-hand with legibility, while I'll admit it's really just a matter of learning what `aif` does, the same way you have to learn what anything else does, I'd argue that a programmer who has never seen `let-if` before would be able to decipher its meaning from the context, whereas a programmer who's never encountered `aif` will be wondering `wtf`.

[1]: Preface to the first edition of SICP.


Oh yes, it's true that there are aren't many different anaphoric macros, though I occasionally run across a useful new one. But the core ones (in Arc: aif, awhen, aand) are useful in many places.

I don't see that famous SICP as applying to this the way you do. Anaphoric macros obviously don't exist to make programs easier to execute—they're there entirely for humans to read, though obviously the humans don't agree about them.

To my mind, wanting a program to be readable by someone who hasn't learned its idioms is like wanting a newspaper to be readable by someone who hasn't learned its words. Some words are easy to guess from context, and that's nice. But others require learning, especially when a text isn't in your native language, and that doesn't make them unreadable.


Well, newspapers are written at a middle-school reading level, or so I'm told.

I disagree that anaphora are for readers. I think they're primarily for writers, as a convenience to save them some typing. The extra time spent reading `let-if` vs. `aif` is negligible, a small fraction of a second at worst.

But, perhaps the disagreement comes from a fundamental difference in what each of us considers "readable". In my mind, readability is measured by the percentage of readers who comprehend what was written without aid. The amount of obviousness that such a goal demands not only requires explicity, but in my opinion it increases the ease of comprehension even for experts (so long as it's not something surprising to them -- e.g., I wouldn't go out of my way to write `let-if` in Arc since (iirc) it already provides `aif`, and that's what's expected by Arc programmers).

On the other hand, I get the feeling that you rather view readability as the maximum of the ease of which each reader could comprehend what was written (not to put words in your mouth, please correct me if I'm wrong). I think that's an equally valid conception of the term, just not one that I share (or even really thought about before, to be honest).


Anaphoric macros certainly don't exist to save typing; they would be worse than useless if that were true, and the cost of typing is negligible in any good Lisp program in any case. I think you might be missing the information they add.

An 'awhen' is easier to read than a 'let-if' because it encodes more information. It communicates something like: "The following form is organized around a single expression of interest. We're going to evaluate that expression and, if it's non-nil, do something with it (where 'it' is literally the name assigned to the value). We needn't name it explicitly, because the expression and what we're going to do with it are both simple and straightforward".

That's a lot of extra information, and it makes 'awhen' semantically much narrower than 'let' (or 'let-if'), which are more general and thus more expensive to read. To read a 'let-if', you must process not only the expression being evaluated but also the name being assigned to it, and there's no implicit guarantee of a single, simple organizing principle to the form. If you get used to them, you'll notice yourself feeling a bit easier when encountering an 'awhen', 'aif' or 'aand' because you instinctively know you won't need to spend as much mental energy to grok it. As sctb put it in a conversation, "When I see an 'aif' or 'aand' form, I have less anticipatory tension than if I see a more general construct like 'let'."

It's a bit like the difference between a simple 'repeat n times' macro and, say, a 'do' loop in Common Lisp, which is much more general and thus takes more energy to read. Consuming less of the reader's mental energy is key for readability—it frees up cognition for understanding the rest of the system.

This all has to do with making code easier to read, not write. Yes it requires a one-time effort to learn what 'awhen' means, and yes the code will be more obscure to anyone who hasn't taken a minute to learn that yet, but those are tiny costs compared to the lightness that's paid back again and again when the construct is used well.

I wouldn't put anaphoric macros in front of a beginning programming student, because learning basic programming is already so hard that any added cost is nontrivial. They're struggling to get up the hill as is, so one shouldn't put any extra tools into their backpack. Moreover, beginning students work only with simple programs where the value of a tool like anaphoric macros doesn't add up to much. But building complex production systems is another matter. There complexity is the arch-enemy, and any tool that lightens cognitive load is an asset.


I have designed an anaphoric if which chooses the "it" antecedent differently from PG's aif.

Moreover, if the expression identified as "it" is a place, then "it" is an alias for the place, whereby the place is evaluated only once even if multiple occurrences occur.

http://www.nongnu.org/txr/txr-manpage.html#N-018F39B0


I disagree that `aif` is semantically any narrower than `let-if`, or that `awhen` is narrower than `let-when`. I'd argue that they have very nearly the same semantics, or at least the same semantic structure, modulo alpha conversion. Furthermore, `it` is implicit, but that does not mean it gets to escape mental processing. It's an extra thing that must be learned and remembered, and a mental binding must be established whenever the form is read anyway.

I also feel confident that pg put the anaphoric macros into Arc primarily to reduce typing, given his introductory article about the design of Arc:

> It would not be far from the truth to say that a hacker about to write a program decides what language to use, at least subconsciously, based on the total number of characters he'll have to type. If this isn't precisely how hackers think, a language designer would do well to act as if it were.

However, I'd be silly to deny that less writing does not lead directly to less reading, and I'd be disingenuous not to also include a later passage:

> But the cost of a long name is not just the cost of typing it. There is also the cost of reading it, and the cost of the space it takes up on your screen.

(Somewhat tangentially, I agree that names in Lisp can sometimes get comically long, but I think there's a happy medium between 20+ character names and 3- character names.)

I personally find the more explicit variable names offered by `let-if` (and the more descriptive macro name itself) to aid in reading. I also disagree that `aif` has a "single, simple organizing principle to the form" whereas `let-if` does not. `let-if` is, as its name implies, a composition of `let` and `if` (just as `mapcar` is a composition of `map` and `car`, and `cadr` is a composition, etc.), and it is structured that way. In fact, even if `aif` &co. are individually fine, they add weight (albeit quite light) to the mental burden of the overall language simply by being extra, whereas `let-if`, as a composition of existing constructs, is not extra.

I'm willing to agree to disagree and chalk it up to preference. In fact, I'm starting to feel a little silly elaborating such a small thing :D I'm just hoping I managed to adequately explain my point of view, and that it at least seems like a sensical place to stand, even if you think it's a worse place to be standing.

And thank you for taking the time to elaborate on your thoughts. A friend of mine with whom I periodically debate religious topics is fond of saying "an unexamined faith is not worth having", and I think the same holds true for any belief, opinion, ideology, etc. At the very least, I had a chance to really think about this stuff in more depth :)


I also don't feel that aif has much value, and doesn't resemble the linguistic anaphora that it is inspired by.

That's why I did a "one up" in my ifa design, to outdo the silly aif macro.

In English, I can say, "if x exceeds 3, then print it". When I say that, it's obvious to everyone that by "it" I do not mean the Boolean value of "x exceeds 3". I mean x. So aif is nothing like the anaphoric "it" we encounter in English.

I tried to make ifa at least superficially try to capture some of that: (ifa (> (whatever) 3) (print it)): prints value of (whatever). Also, suppose (whatever) is a place. We want to be able to say "If (whatever) exceeds 3, increment it": (ifa (> (whatever) 3) (inc it)). And we don't want (whatever) evaluated twice.

The expression (aif whatever expr-containing-it) doesn't buy us much over (iflet (var whatever) expr-containing-var). Both communicate an organization of code around something returned from whatever, which can be nil (don't run the code) or some non-nil object (do run the code, refer to the thing as var). To me also, aif does look mostly like keystroke saving.

My iflet/whenlet/whilet/condlet constructs bind multiple variables. The last one is tested: (iflet ((x whatever) (y another thing)) expr-evaled-if-y-true). This makes the macros helpful in more situations. (Next release will make the y optional if expr doesn't need it.)

E.g.:

     (whenlet ((x (get-something arg))
               (y (get-some-dependent-thing x other-arg (calculation)))
       ;; code run if y is true, with access to x too
       )
Or:

     (whilet ((next (get-next-object stream))
              (maybe-foo (if next (get-foo-property next))))
      ;; process only maybe-foo things from stream
      )
We should try towrite macros that have some half decent level of impact: bang for the parenthesis and symbol. Especially if they are intended for enduring public use.


Very neat!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: