Domain-Specific Languages

Techniques: macros (syntax), data-as-code (interpret s-expression data), reader macros (Common Lisp custom syntax), #lang languages (Racket: full custom languages with their own readers).

Famous Lisp DSLs: the CL loop macro, Racket's Scribble, Clojure's core.async go, Emacs use-package, SQL DSLs (HoneySQL, Postmodern's S-SQL), and build/config DSLs (Guix, Nix-like Scheme configs).

;; Embedded DSL via macros: a state machine
(define-syntax define-state-machine
  (syntax-rules (->)
    ((_ name (state (input -> next) ...) ...)
     (define (name current in)
       (case current
         ((state) (case in ((input) 'next) ... (else 'error))) ...
         (else 'error))))))

(define-state-machine traffic
  (red    (timer -> green))
  (green  (timer -> yellow) (emergency -> red))
  (yellow (timer -> red)))

(traffic 'red 'timer)                    ; green
(traffic 'green 'emergency)              ; red
;; Data-driven DSLs (Clojure style): describe with data, interpret with functions
(def routes [["/users" {:get :list-users :post :create-user}]
             ["/users/:id" {:get :get-user}]])
;; Hiccup: HTML as vectors
[:div {:class "card"} [:h1 "Title"] [:p "Body"]]
;; Common Lisp: an HTML DSL macro (simplified)
(defmacro html (&body forms)
  `(with-output-to-string (s)
     ,@(mapcar (lambda (f) `(format s "~a" ,(render-form f))) forms)))