6 ms·
I suspect you might be viewing the ideas of macros and meta-programming at slightly the wrong abstraction level. Macros are not merely about making code more le
by AKrumbach 11y ago
I suspect you might be viewing the ideas of macros and meta-programming at slightly the wrong abstraction level. Macros are not merely about making code more legible or terse -- I would indeed agree that is a pure anti-pattern. Depending on the nature of your project and team, creating a series of DSL macros is almost certainly more work than it's worth.
However, there is one usage of macros I will never give up, and which has turned me off from any languages which cannot do meta-programming: macros as pattern definition. The "canonical" example of this would be resource management. Languages like C# or Java have the try ... finally syntax, and while this is great to ensure you don't accidentally leave file handles open, it only takes forgetting once to have resource issues. Macros provide the easiest to implement answer: just write a little "with-file" macro wrapper for the proper calls, and you don't need to think about the problem of open file handles again.
In fact, these sort of "mini-macros" are best for addressing the problems of concern separation, legibility, maintainability, and refactoring: the "with-file" macro is a prime example of separating the concern of managing file handles from the code which uses the file. You either won't want to refactor the way files are handled, or you'll want to do it everywhere at once -- a well-defined macro eases this effort. Finally, the questions of legibility and maintenance are best "shunted" to properly documenting the macro as you would any other function in your project (e.g. use of ruby-doc or other IDE annotation features).
- sklogic 11y ago> Depending on the nature of your project and team, creating a series of DSL macros is almost certainly more work than it's worth. Initially - maybe. But it reduces the maintenance cost significantly. You'll write more code in the first place, yes, but it will be a much simpler code than a denser ad hoc solution, it will serve a better documentation for the problem you've been solving, and it will be easier to read, debug and change in the future.
- skybrian 11y agoJust as a side point, Java 7 and above has a try block with special handling of resources [1]. That gives you most of what you want, except that a lot of older Java code isn't using it yet. (Then again, the same would be true of a macro if it were fairly new and there's code that hasn't been converted to use it yet.) [1] https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html https://docs.oracle.com/javase/tutorial/essential/exceptions...
- Strilanc 11y agoI don't understand your example. Why is it necessary for with-file to me a macro? It seems like any language with lambdas could write it as a higher-order function that asked for a consumer. How does having a with-file macro fix the "what if you forget it?" issue? It seems like you can forget with-file as easily as you can forget `using`.
- pg314 11y agoThe with-x style macros can indeed be replaced by functions that take a closure. E.g. in Scheme you have the function (call-with-input-file string proc). The macro is slightly more concise. Contrast (with-input-file (x "x") (print (read x))) ; Common Lisp with (call-with-input-file "x" (lambda (x) (print (read x)))) ; Scheme
- chaoky 11y agoNo runtime consing of closures either! In fact, in Common Lisp there is a style of writing call-with-foo that takes a closure and wrapping that with a macro with-foo. Since the macro abstracts that detail of having call-with, it is much more flexible. For example the macro could expand some stuff known at compile time and do some compile time computation too., instead of the closure consing function.
- AKrumbach 11y agoI feel pg314 did an adequate job answering your first question -- although I would have explained it that any meta-programming macros must be, by definition, higher-order functions. The only differences are giving the consumer function inline syntax and partial execution at compile time: both potent benefits. With-file might not be the very best example of "what if you forget", because it's so very simple. A more complex example -- where "forgetting" might be more than just a single function call -- would be handling semaphores and locks for threading. A well-written "with-lock" macro could ensure that these resources are obtained and released in a determined order (i.e. compute some sort of hierarchy for locks and enforce this when obtaining multiple locks).
- lmm 11y agoA good language should have the generic features necessary to do that kind of resource management without needing macros. See e.g. https://github.com/jsuereth/scala-arm https://github.com/jsuereth/scala-arm - and note that it's all just ordinary code.
- ICWiener 11y agoMacros are not needed. You only need the UNWIND-PROTECT special operator, which is the generic feature you are looking for.