Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

My favorite example for this is the lame idiom you see in Java code:

    if (log.isDebugEnabled()) {
        log.debug("expensive" + debug + message);
    }
This is "better" than just log.debug(...) because with the latter, your expensive log message argument needs to be evaluated even if debug is disabled.

However, in a language w/ macros, you just say:

    (debug (str "expensive" debug message))
and these considerations are already taken care of for you:

https://github.com/clojure/tools.logging/blob/master/src/mai...



Edit: Ohhh! I see! The message isn't evaluated until after! Got it.

I don't understand why this is special?

Why can't you rewrite log.debug to include the check?

    func debug(m string) {
        if log.isDebugEnabled() {
            log.debug(m);
        }
    }
What makes macros special in this case?


The point was that "debug" and "message" parts in the example were arbitrarily complex expressions which were computationally expensive to evaluate. Your wrapping of log debug would still require evaluating them to get the message string unconditionally, even when debug logging is not enabled.

Of course, with support for first-class functions, you could do something like:

  func debug(produceMessage ()->string) {
    if log.isDebugEnabled() {
      log.debug(produceMessage());
    }
  }


And then what would I need to type to write a log message?


You pass a lambda that returns the debug message. With javascripty syntax:

    debug( function(){ return "my expensive log message" })
BTW, if your language is lazily evaluated (like Haskell) then you don't need to do this because arguments will only ve evaluated when they are needed.


The downside there is you can still end up allocating a closure. Whereas an expanded macro shouldn't cost anything. (A sufficiently smart compiler might be able to optimize the closure allocation.)


A sufficiently modern language (like, saaaaay, D2) could also give you a type like "closure you don't intend to escape" (let's call this a "scoped closure", or if you will, "scope string delegate()"), and eschew allocation entirely without requiring optimization.


For completeness of the argument, this particular problem is solved in the Java world with string formats. With the slf4j interface, that would be:

log.debug("expensive %s %s", debug, message)

The message is not actually formatted into one string unless the DEBUG trace level is enabled. Of course, you are still passing the arguments around, but with object references that's a negligable difference.

I still appreciate the solid example of a problem macros are good at solving, though. Two ways around one problem.


What if it's debug() instead of debug? That could be an expensive function that does a bunch of things to produce the debug output.


Solid point, that would be a downside of the slf4j approach.


It's not solved, because method arguments are evaluated eagerly. It means that message argument may not be an expensive expression, because it will be calculated independently whether debug is enabled or not. In case of macros arguments of this method could be evaluated lazily.


Your lame idiom example in java is why I love Lua so much:

    log.debug(debugIsEnabled and "yo expensive" or "yikes!")


Calling log.debug() with and argument of `false` is a no-op? That sounds like someone bending the language to fit an idiom, because it doesn't sound like a sane API except that it enables this use case.


It is insane in an eager-evaluated calling context. You're not wrong. But in Lisp it's not insane at all to let a macro consume a parameter and yield a no-op. Think of how this plays with a JIT and having code that can dynamically switch between dev/QA/production behavior and performance profiles, just for one example.


#define DEBUG(fmt, ...) if (DEBUG) { printf(fmt, ...);}

?

(change syntax to actually work)


That does something similar, sure, but what it in fact is is a macro. As snikeris was saying, you couldn't write anything quite like that #define in a macroless language like Java.




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

Search: