5 ms·
My favorite example for this is the lame idiom you see in Java code: if (log.isDebugEnabled()) { log.debug("expensive" + debug + message); } T
by snikeris 11y ago
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/main/clojure/clojure/tools/logging.clj#L91 https://github.com/clojure/tools.logging/blob/master/src/mai...
- TenDecigrams 11y agoEdit: 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?
- dragonwriter 11y agoThe 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()); } }
- snikeris 11y agoAnd then what would I need to type to write a log message?
- ufo 11y agoYou 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.
- MichaelGG 11y agoThe 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.)
- FeepingCreature 11y agoA 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.
- GFK_of_xmaspast 11y ago#define DEBUG(fmt, ...) if (DEBUG) { printf(fmt, ...);} ? (change syntax to actually work)
- jeorgun 11y agoThat 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.
- kedean 11y agoFor 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.
- nulltype 11y agoWhat if it's debug() instead of debug? That could be an expensive function that does a bunch of things to produce the debug output.
- kedean 11y agoSolid point, that would be a downside of the slf4j approach.
- mateuszf 11y agoIt'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.
- fit2rule 11y agoYour lame idiom example in java is why I love Lua so much: log.debug(debugIsEnabled and "yo expensive" or "yikes!")
- sirclueless 11y agoCalling 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.
- spacemanmatt 11y agoIt 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.