{"slug": "will-it-defmacro", "title": "Will it DEFMACRO?", "summary": "A developer who used an LLM to build a rogue-like game in Common Lisp found that the model generated a macro to define armory items, reducing boilerplate code. The macro, named define-armory-equippable-item, creates item classes and factories with customizable parameters, demonstrating the model's ability to apply Lisp idioms.", "body_md": "Why write boilerplate code when you can ask an LLM can do it for you? But even LLMs will avoid boilerplate if they can. I recently vibe coded a rogue-like game in Common Lisp. While I gave the model specific directions at certain points, I mostly let the model generate the code as it saw fit. I found some interesting suprises in the generated code.\n\nOne feature of these sorts of games is that there is a lot of varied `stuff` you can encounter. This keeps the game interesting and maintains the novelty and sense of discovery as it takes a long time for the player to discover everything about the game. While it is fun to think of all of the different things to put in the game, it is a bit tedious to actually implement them all. You want a large variety of things, they should all be different in more than just their description, and the effect of encountering and using an item should be appropriate to the kind of item it is. It would be boring if every food item simply retored 5 health, but much more entertaining if some food restored health, some restored stamina, some made the player stronger, etc., and even better if it were spinach that restored strength, an elixir that restored health, and an energy drink that restored stamina.\n\nSo I prompted the model with a few examples of the kinds of things I wanted to see in the game and told it to extend my list with its own ideas, and to implement them in the game.\n\nTypically, this is where the tedium sets in because you need to write a lot of boilerplate code that just has subtle variations. LLMs are good at boilerplate, so I expected to find that. But an experienced Common Lisp programmer would write a macro to generate the boilerplate based on a few customization parameters. I was pleasently surprised to see that the model did exactly this.\n\n```\n(defmacro define-armory-equippable-item (class-name display-name equip-slot stat-bonuses documentation\n                                          &key (weapon-reach 1) (weapon-hits-per-turn 1) on-hit-effect)\n  \"Define a stateless EQUIPPABLE-ITEM subclass CLASS-NAME and its\nmatching MAKE-CLASS-NAME factory. The repetitive §13 armory content is\nall fixed-data leaf classes like STACK-OF-UNREAD-MEMOS, so a single\nmacro keeps their definitions uniform without introducing any runtime\nregistry or mutable catalog layer. The generated factory accepts a\n&KEY MODIFIER (default :NORMAL), passed straight through as the new\ninstance's own ITEM-MODIFIER -- see EQUIPPABLE-ITEM's own docstring\nfor what :CURSED/:BLESSED do -- and &KEY CLOAKED (default T), passed\nstraight through as the new instance's own ITEM-CLOAKED-P (see\nEQUIPPABLE-ITEM's own docstring for what cloaking hides).\"\n  (let ((factory-name (intern (format nil \"MAKE-~A\" (symbol-name class-name)) (symbol-package class-name))))\n    `(progn\n       (defclass ,class-name (equippable-item)\n         ()\n         (:default-initargs :name ,display-name\n                            :equip-slot ,equip-slot\n                            :stat-bonuses ,stat-bonuses\n                            :weapon-reach ,weapon-reach\n                            :weapon-hits-per-turn ,weapon-hits-per-turn\n                            :on-hit-effect ,on-hit-effect)\n         (:documentation ,documentation))\n       (defun ,factory-name (&key (modifier :normal) max-durability durability (cloaked t))\n         ,(format nil \"Pure factory: return a fresh ~A. MODIFIER (default :NORMAL) is passed\nstraight through as the new instance's own ITEM-MODIFIER. CLOAKED\n(default T) is passed straight through as the new instance's own\nITEM-CLOAKED-P (see EQUIPPABLE-ITEM's own class\ndocstring). MAX-DURABILITY/DURABILITY (default NIL, meaning \\\"use\nEQUIPPABLE-ITEM's own class default/derive from MAX-DURABILITY\\\" --\nsee its class docstring) are only forwarded to MAKE-INSTANCE when\nexplicitly supplied, so a direct/test call keeps this item's usual\ndeterministic *RDESCENT-DEFAULT-ITEM-DURABILITY*.\" display-name)\n         (apply #'make-instance ',class-name :modifier modifier :cloaked cloaked\n                (append (when max-durability (list :max-durability max-durability))\n                        (when durability (list :durability durability))))))))\n```\n\nThis macro generates the class definition for the item and a factory function to create instances of the item. The required arguments are common to all equippable items, and the optional arguments are modifiers that are only relevant to certain items. This macro is used to define each item that can be equipped by the player.\n\n```\n(define-armory-equippable-item branded-corporate-yeti-mug\n  \"Branded Corporate Yeti Mug\"\n  :off-hand\n  (list :caffeine-tolerance 3)\n  \"Off-hand mug granting +3 :CAFFEINE-TOLERANCE, which today feeds the\nalready-wired kombucha healing formula through EFFECTIVE-CAFFEINE-\nTOLERANCE. Its planned refill/drain-rate behavior is deliberately\ndeferred because no passive CAFFEINE-TOLERANCE depletion system exists\nyet.\"\n  )\n\n(define-armory-equippable-item lanyard-of-the-vip\n  \"Lanyard of the VIP\"\n  :head\n  (list :seniority 2)\n  \"Neck-flavored accessory implemented in the shared :HEAD slot, with a\nreal +2 :SENIORITY bonus feeding deflection/detection formulas. Its\nplanned \\\"SecOps Auditor aggro radius to zero\\\" behavior is deliberately\ndeferred because that monster/archetype-specific aggro mechanic does\nnot exist yet.\"\n  )\n```\n\nSimple uses of the macro are straightforward you specify class name, the location in which it can be equipped, what stats are modified by equipping the item, and a docstring.\n\n```\n (define-armory-equippable-item red-swingline-stapler\n  \"Red Swingline Stapler\"\n  :weapon\n  (list :power 2)\n  \"Low-damage weapon with a 10% on-hit :BLEED effect. :BLEED is wired\nfor real as a small damage-over-time effect via STATUS-EFFECT's own\nMAGNITUDE slot; the plan text's additional \\\"panic the target\\\" rider\nis deliberately deferred because the current AI has no temporary panic\nstatus that can cleanly override disposition/pathing.\"\n  :on-hit-effect (list :kind :bleed :turns *rdescent-bleed-ticks*\n                       :magnitude *rdescent-bleed-damage-per-tick* :chance 0.10))\n```\n\nThis item has a special effect that is applied when it hits an enemy. The optional argument to the macro allows you to specify the effect. Note that the model understood the semantic context of the weapon (staples puncture, punctures bleed) and it invented the `:bleed` effect on its own and implemented the mechanics of it within the game.\n\n```\n(define-armory-equippable-item three-foot-ethernet-cable\n  \"3-Foot Ethernet Cable (Cat 6)\"\n  :weapon\n  (list :power 2)\n  \"Fast whip-style weapon: low damage, two hits per attack action, and\nreal reach 2 through WEAPON-REACH/WEAPON-HITS-PER-TURN. The plan text's\ncrowd-control flavor is therefore approximated through the existing\ncombat scheduler rather than a new knockback or entangling subsystem.\"\n  :weapon-reach 2\n  :weapon-hits-per-turn 2)\n```\n\nThis weapon uses the optional arguments to specify a longer reach and faster attack speed than standard weapons. Again, note that this is appropriate for the item.\n\nThe model generated twenty-eight different equippable items of various types with varying bonuses and effects. This illustrates the model's ability to effectively use macros to reduce the boilerplate code that would otherwise be necessary to implement items. It also illustrates that the model understands both the theme of the game and the nature of the items being implemented.\n\nMost items in the game can be discovered just sitting around on the ground, so most items have `ground` wrapper that provides an object that occupies a tile on the map.\n\n```\n(defmacro define-ground-armory-item (name item-factory char color)\n  \"Define the MAKE-GROUND-* wrapper corresponding to ITEM-FACTORY for a\n§13 equippable item.\"\n  (let* ((item-name (symbol-name item-factory))\n         (prefix-length (length \"MAKE-\"))\n         (suffix (subseq item-name prefix-length))\n         (ground-name (intern (format nil \"MAKE-GROUND-~A\" suffix) (symbol-package item-factory))))\n    `(defun ,ground-name (x y level)\n       ,(format nil \"Pure factory: return a fresh GROUND-ITEM wrapping ~A.\" name)\n       (make-ground-equippable-item x y level ,char ,name ,color (,item-factory)))))\n\n(define-ground-armory-item \"Red Swingline Stapler\" make-red-swingline-stapler #\\) \"#d08770\")\n(define-ground-armory-item \"3-Foot Ethernet Cable (Cat 6)\" make-three-foot-ethernet-cable #\\) \"#d08770\")\n(define-ground-armory-item \"Lanyard of the VIP\" make-lanyard-of-the-vip #\\] \"#b48ead\")\n(define-ground-armory-item \"Branded Corporate Yeti Mug\" make-branded-corporate-yeti-mug\n  *rdescent-corporate-trinket-char* \"#8fbcbb\")\n```\n\nThis macro generates the function name for the ground item based on the name of the factory function for the item. So if `make-red-swingline-stapler` is the factory for the stapler, then `make-ground-red-swingline-stapler` is the factory for the ground item that wraps the stapler. The macro also generates a docstring for the function.\n\nI told the model that it was allowed to use the Latin-1 character set so that it would have a larger set of characters to choose from when selecting a character to represent the item on the map. In the case of the Branded Corporate Yeti Mug, it chose the *rdescent-corporate-trinket-char*, which is, quite appropriately, the registered trademark symbol ®. Again this indicates that the model understood the theme of the game.\n\nThe model of course saved hours of tedious typing, but by generating macros to define items, it reduced the actual boilerplate and increased the maintainability of the code.", "url": "https://wpnews.pro/news/will-it-defmacro", "canonical_source": "https://funcall.blogspot.com/2026/09/will-it-defmacro.html", "published_at": "2026-09-04 07:00:00+00:00", "updated_at": "2026-09-04 07:22:02.467032+00:00", "lang": "en", "topics": ["artificial-intelligence", "generative-ai", "developer-tools"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/will-it-defmacro", "markdown": "https://wpnews.pro/news/will-it-defmacro.md", "text": "https://wpnews.pro/news/will-it-defmacro.txt", "jsonld": "https://wpnews.pro/news/will-it-defmacro.jsonld"}}