Example: Macro Examples
Source:vignettes/articles/examples-macro-examples.Rmd
examples-macro-examples.RmdMacro definitions, quasiquote, expansion, and DSL-style helpers.
Source: https://github.com/wwbrannon/arl/blob/main/inst/examples/macro-examples.arl
arl> ;; Macro System Examples
arl> ;; Demonstrates defmacro, quasiquote, unquote, and macro expansion
arl> (import assert :refer :all)
arl> (import binding :refer :all)
arl> (import control :refer :all)
arl> (import display :refer :all)
arl> (import looping :refer :all)
arl> (import threading :refer :all)
arl> ;; ============================================================================
arl> ;; Simple Macros
arl> ;; ============================================================================
arl> (println "=== Simple Macros ===\n")
#> "=== Simple Macros ===
#> "
arl> ;; A macro that doubles its argument
arl> ;;' @description Multiply x by 2 at macro expansion time.
arl> (defmacro double (x)
arl> `(* 2 ,x))
arl> (assert-equal 10 (double 5))
#> TRUE
arl> (println (string-concat "(double 5) = " (double 5)))
#> "(double 5) = 10"
arl> (assert-equal 6 (double (+ 1 2)))
#> TRUE
arl> (println (string-concat "(double (+ 1 2)) = " (double (+ 1 2))))
#> "(double (+ 1 2)) = 6"
arl> ;; A macro that creates a variable with a default value
arl> ;;' @description Define a variable with a value.
arl> (defmacro defvar (name value)
arl> `(define ,name ,value))
arl> (defvar x 42)
#> 42
arl> (assert-equal 42 x)
#> TRUE
arl> (println (string-concat "x = " x))
#> "x = 42"
arl> ;; ============================================================================
arl> ;; Macros with Multiple Expressions
arl> ;; ============================================================================
arl> (println "\n=== Macros with Multiple Expressions ===\n")
#> "
#> === Macros with Multiple Expressions ===
#> "
arl> ;; A macro that executes multiple forms and returns the last
arl> ;;' @description Evaluate forms in order and return the last.
arl> (defmacro progn (first . rest)
arl> `(begin ,first ,@rest))
arl> (println "Using progn:")
#> "Using progn:"
arl> (progn
arl> (println "First")
arl> (println "Second")
arl> (println "Third"))
#> "First"
#> "Second"
#> "Third"
arl> ;; ============================================================================
arl> ;; Control Flow Macros
arl> ;; ============================================================================
arl> (println "\n=== Control Flow Macros ===\n")
#> "
#> === Control Flow Macros ===
#> "
arl> ;; Custom when-not macro
arl> ;;' @description Evaluate body when test is falsy.
arl> (defmacro when-not (test . body)
arl> `(if (not ,test)
arl> (begin ,@body)
arl> #nil))
arl> (when-not #f
arl> (println "This should print"))
#> "This should print"
arl> (when-not #t
arl> (println "This should not print"))
arl> ;; A do-times macro
arl> ;;' @description Repeat body n times.
arl> (defmacro do-times (n . body)
arl> (let ((counter (gensym "counter")))
arl> `(let ((,counter 0))
arl> (while (< ,counter ,n)
arl> (begin ,@body)
arl> (set! ,counter (+ ,counter 1))))))
arl> (println "do-times example:")
#> "do-times example:"
arl> (define count 0)
#> 0
arl> (do-times 5
arl> (set! count (+ count 1))
arl> (println (string-concat "Count: " count)))
#> "Count: 1"
#> "Count: 2"
#> "Count: 3"
#> "Count: 4"
#> "Count: 5"
arl> (assert-equal 5 count)
#> TRUE
arl> ;; ============================================================================
arl> ;; Macro Expansion Examples
arl> ;; ============================================================================
arl> (println "\n=== Macro Expansion ===\n")
#> "
#> === Macro Expansion ===
#> "
arl> (println "Original: (when (> 5 3) (println \"yes\"))")
#> "Original: (when (> 5 3) (println "yes"))"
arl> (println "Expanded:")
#> "Expanded:"
arl> (println (macroexpand-1 '(when (> 5 3) (println "yes"))))
#> (if (> 5 3) (begin (println "yes")) .__nil)
arl> (println "\nOriginal: (let ((x 1)) (+ x 2))")
#> "
#> Original: (let ((x 1)) (+ x 2))"
arl> (println "Expanded:")
#> "Expanded:"
arl> (println (macroexpand-1 '(let ((x 1)) (+ x 2))))
#> ((lambda (tmp__17) (define x tmp__17) (begin (+ x 2))) 1)
arl> ;; ============================================================================
arl> ;; Anaphoric Macros (macros that capture a name)
arl> ;; ============================================================================
arl> (println "\n=== Anaphoric Macros ===\n")
#> "
#> === Anaphoric Macros ===
#> "
arl> ;; An anaphoric if that binds result to 'it'
arl> ;;' @description Bind test result to it and select then/alt.
arl> (defmacro aif (test then alt)
arl> `(let ((it ,test))
arl> (if it ,(capture 'it then) ,(capture 'it alt))))
arl> (println "Anaphoric if example:")
#> "Anaphoric if example:"
arl> (aif (+ 2 3)
arl> (println (string-concat "Result is: " it))
arl> (println "No result"))
#> "Result is: 5"
arl> ;; ============================================================================
arl> ;; Building DSLs with Macros
arl> ;; ============================================================================
arl> (println "\n=== Simple DSL Example ===\n")
#> "
#> === Simple DSL Example ===
#> "
arl> ;; A macro for defining test cases
arl> ;;' @description Define and run a simple test case.
arl> (defmacro deftest (name . body)
arl> `(begin
arl> (println (string-concat "Running test: " (quote ,name)))
arl> ,@body
arl> (println "Test passed!\n")))
arl> (deftest addition-works
arl> (assert (= (+ 1 2) 3) "1 + 2 should equal 3")
arl> (assert (= (+ 5 5) 10) "5 + 5 should equal 10"))
#> "Running test: addition-works"
#> "Test passed!
#> "
arl> (deftest string-operations
arl> (assert (= (string-concat "hello" " " "world") "hello world")
arl> "String concatenation works"))
#> "Running test: string-operations"
#> "Test passed!
#> "
arl> ;; ============================================================================
arl> ;; Hygiene and explicit capture
arl> ;; ============================================================================
arl> (println "\n=== Hygiene and Capture ===\n")
#> "
#> === Hygiene and Capture ===
#> "
arl> ;; Automatic hygiene (no gensym needed)
arl> ;;' @description Swap two bindings using a temporary.
arl> (defmacro swap (a b)
arl> `(let ((temp ,a))
arl> (set! ,a ,b)
arl> (set! ,b temp)))
arl> (println "Demonstrating gensym:")
#> "Demonstrating gensym:"
arl> (println (string-concat "Generated symbol 1: " (gensym)))
#> "Generated symbol 1: G__23"
arl> (println (string-concat "Generated symbol 2: " (gensym)))
#> "Generated symbol 2: G__24"
arl> (println (string-concat "Generated symbol with prefix: " (gensym "my-prefix")))
#> "Generated symbol with prefix: my-prefix__25"
arl> ;; ============================================================================
arl> ;; Quasiquote Examples
arl> ;; ============================================================================
arl> (println "\n=== Quasiquote Examples ===\n")
#> "
#> === Quasiquote Examples ===
#> "
arl> (define x 10)
#> 10
arl> (define y 20)
#> 20
arl> (println "Without quasiquote:")
#> "Without quasiquote:"
arl> (println (list '+ 1 2))
#> (+ 1 2)
arl> (println "\nWith quasiquote and unquote:")
#> "
#> With quasiquote and unquote:"
arl> (println `(+ ,x ,y))
#> (+ 10 20)
arl> (println "\nWith unquote-splicing:")
#> "
#> With unquote-splicing:"
arl> (define nums (list 1 2 3))
#> (1 2 3)
arl> (println `(+ ,@nums)) ; Should expand to (+ 1 2 3)
#> (+ 1 2 3)
arl> ;; ============================================================================
arl> ;; Recursive Macros
arl> ;; ============================================================================
arl> (println "\n=== Recursive Macro Example ===\n")
#> "
#> === Recursive Macro Example ===
#> "
arl> ;; A macro that chains function calls
arl> ;;' @description Thread value through forms (first argument).
arl> (defmacro -> (value . forms)
arl> (if (null? forms)
arl> value
arl> (let ((first-form (car forms))
arl> (rest-forms (cdr forms)))
arl> (if (list-or-pair? first-form)
arl> `(-> (,(car first-form) ,value ,@(cdr first-form)) ,@rest-forms)
arl> `(-> (,first-form ,value) ,@rest-forms)))))
arl> (println "Threading macro example:")
#> "Threading macro example:"
arl> (assert-equal 15 (-> 5
arl> (+ 3)
arl> (* 2)
arl> (- 1)))
#> TRUE
arl> (println (string-concat "Result: " (-> 5
arl> (+ 3)
arl> (* 2)
arl> (- 1)))) ; ((5 + 3) * 2) - 1 = 15
#> "Result: 15"
arl> ;; ============================================================================
arl> ;; Compile-Time Computation
arl> ;; ============================================================================
arl> (println "\n=== Compile-Time Computation ===\n")
#> "
#> === Compile-Time Computation ===
#> "
arl> ;; A macro that computes at macro expansion time
arl> ;;' @description Compute the product of a and b at expand time.
arl> (defmacro const-multiply (a b)
arl> (let ((result (* a b)))
arl> `(quote ,result)))
arl> (assert-equal 42 (const-multiply 6 7))
#> TRUE
arl> (println (string-concat "(const-multiply 6 7) = " (const-multiply 6 7)))
#> "(const-multiply 6 7) = 42"
arl> (println "This multiplication happened at macro expansion time!")
#> "This multiplication happened at macro expansion time!"
arl> (define threading-result (-> 5 (+ 3) (* 2) (- 1)))
#> 15
arl> (define aif-result (aif (+ 2 3) it #nil))
#> 5
arl> (assert-equal 15 threading-result)
#> TRUE
arl> (assert-equal 5 aif-result)
#> TRUE
arl> (println "\nExample complete!")
#> "
#> Example complete!"