4 ms·
Can you give us a couple of examples of how lisp macros "abstract away" patterns?
by kyberias 10y ago
Can you give us a couple of examples of how lisp macros "abstract away" patterns?
- TeMPOraL 10y agoTake the example from [0]: > Also - there are such "nonexciting patterns" in OOP languages as well, and they are much less often abused. For example "a switch inside a while" pattern. Which I think I will need to name "StateMachinePattern" to make it cool and stop people refactoring it into strategies and stuff. > There is value in one-screen definition of the whole machine, instead of separating it into 6 files. In Lisp you could create a macro, say, define-state-machine, and use it like that: (define-state-machine :my-state-machine ((:state-1 (do-something) (do-something-else) (if (condition) (transition :state-2) (transition :state-3))) (:state-2 (do-something) (transition :state-3)) (:state-3 (do-something) (when (should-quit) (quit-state-machine))))) This macro could easily expand to a "switch inside a while" (or, possibly, to a "let over lambda over cond", or maybe even into a series of low-level tagbody and go constructs). The resulting abstraction is clean and communicates its meaning well. -- Different example - when writing macros in Common Lisp, there are two things one usually has to be wary of - unwanted variable capture, and unwanted re-evaluation. So you might end up manually assigning gensyms[1] to avoid variable capture, and manually creating lexical environments to evaluate passed forms only once. But you can also abstract it away! For instance, with with-gensyms and once-only macros from Alexandria[2]. Or you could build something like defmacro! described in Let Over Lambda[3], i.e. a macro that automatically code-walks your macro definition and extracts variables named g!foo and o!bar to apply gensyms and once-only to them, respectively. -- Those are only two examples, but the general idea is - whenever you see yourself repeating the same boilerplate in similar places to express a concept, you can wrap that boilerplate inside a macro and make the compiler generate it for you. Since Lisp makes all of its capabilities available for you during macroexpansion, you can continue this until you're satisfied that your code is readable, boilerplate-free and clearly expresses your intentions. -- [0] - https://news.ycombinator.com/item?id=11730248 https://news.ycombinator.com/item?id=11730248 [1] - gensyms are symbols generated on demand, that are guaranteed to have no possible collision with anything ever [2] - https://common-lisp.net/project/alexandria/draft/alexandria.html#Macro-Writing https://common-lisp.net/project/alexandria/draft/alexandria.... [3] - http://letoverlambda.com/index.cl/guest/chap3.html#sec_6 http://letoverlambda.com/index.cl/guest/chap3.html#sec_6
- ArkyBeagle 10y agoI use this sort of thing in Tcl ( for path-dependent reasons ) A Whole Lot. You can have arrays, indexed by state, and use the "eval" operator to "eval $fsm($state)" fsm is an array of strings, indexed by whatever. I have made dynamic state machine generators this way, although it gets kinda disorienting. The tactical advantage to Tcl (for me) is that it has serial ports and sockets as first-class objects with essentially identical semantics. I work in the embedded space, and this seems 1) unusual and 2) a very nice thing to have to write comprehensive test rigs. I rather like it better because it's all in the "string" & "list" domain rather than the lambda domain. I really should try this in Lisp just to see how wierd it gets.