Hacker News new | past | comments | ask | show | jobs | submit login
Leaky Clojure Macros (theatticlight.net)
47 points by astine on March 1, 2012 | hide | past | favorite | 11 comments



    to read or debug code with them in it one 
    has to under­stand them
Perhaps I'm missing some subtlety to the post, but when was that last time that you were able to take a pass on understanding while debugging?

I think what the author is looking for is better compile-time checking and error reporting in the the with-* macros. I'm not sure how to respond to the "understanding" points. Is the need to understand your language constructs considered a sign of leakiness? I would say no.


> when was that last time that you were able to take a pass on understanding while debugging?

When I run a program I've written in C or Python, the only program that's really running is written in x64 machine code, because that's the only language the processor understands. But I can debug my program without having to understand the x86 instruction set, because high level languages abstract it out.

But if the abstraction was leaky, OTOH, I would still have to understand the details of what's being purportedly abstracted out, so the abstraction wouldn't be properly doing the job of an abstraction.

That's why leaky abstractions are a bad thing.


Author here.

It would seem "understanding" might be the wrong word. If I were to write something like:

    (+ 'foo 1)
I wouldn't have to know how "+" is implemented to find the bug in my code. I would if I were to write:

    (with-open [foo some-object-that-isnt-a-stream]
      ...do stuff...)
however, I would have to know the expansion.

If I have to know the details of the expansion of a macro in order to use it correctly, then it is almost by definition leaky.

In the case of "with-open," a simple type check at the beginning of the macro expansion would solve the problem.


You don't need to know how it's implemented, you just need to know that you have to pass an object that can be closed. You can get that simply by reading the docs.

The exception that's thrown in your first example above certainly makes it blatantly obvious what you did wrong, which is nice. but you still have to know that the addition operator doesn't let you concatenate numbers and symbols in order to use it effectively, or your program will crash once you run it. That's on you.


I can feel a bit of his pain, though. I think some of the trouble is that there are things that should be closable, but the (.close x) isn't enough. A Closable protocol could help with some of this.

Granted, laziness still makes this issue very, very tricky.


Hm I do not agree with you re: `->` and `->>`, and have had no problems with Clojure's context-managing fns (though my experience is limited). Not sure how they're "leaky" at all. They do require a little getting used to but once you understand what's happening they're great.

I say that mostly as someone who is very picky about how his code looks, especially with regard to whitespace, keeping line length under 80 columns, and so forth. The threading macros are great for presenting code in a very readable way.


Yes, and I also think that this discussion needs to mention the facilities Clojure provides to help you with macros.

For example, with simpler macros like -> and ->>, I recommend that curious people macroexpand them, to get a feel for what they mean. Your GUI can make this even easier, like if I put the cursor at the beginning of the following and hit M-x slime-macroexpand-all:

  (-> my_table
      (database db/*stage-db*)
      select*
      exec)
I see:

  (exec (select* (database my_table *stage-db*)))
Or if I do M-. , I get to see the sourcecode. This of course is daunting to beginners, but they can easily mess with the sourcecode, tear it apart, and get to the level where they can just kind of scan smaller macros and get a rough idea of how it's doing its job.

(I realize the author probably knows these things. But I definitely consider -> and ->> to be such a readability win, and hope that people like them.)


Something missing from the article:

    user> (doc with-open)
    -------------------------
    clojure.core/with-open
    ([bindings & body])
    Macro
      bindings => [name init ...]

      Evaluates body in a try expression with names bound to the values
      of the inits, and a finally clause that calls (.close name) on each
      name in reverse order.


The documentation for with-open explicitly states that the expanded form calls .close on the provided names in reverse order. I'm not sure I get how this is a leaky abstraction. Would the author simply prefer to not have to read the docs before using a macro?


Just to state the obvious. With-open respects the close pattern, not the Closeable interface. Plenty of java resources observe the former but not the latter e.g. jdbc Connection interfaces.

I was hoping the article would be about legitimate problems with macros. They do exist...


Plus also, did you guys hear that until just recently, Rails used PUT instead of PATCH?!




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

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

Search: