Share your favourite macros that you've written!

I'm curious to see the macros others have written. :slight_smile:

5 Likes

A good place to start: Syntax Parse Examples which purpose was exactly to answer this question.

A super simple macro I'm using quite often:

(define-syntax-rule (ids->assoc id ...)
  (list (cons 'id id) ...))

Usage example:

(define x 3)
(define caps 'CAPS)
(ids->assoc x caps) ; -> '((x . 3) (caps . CAPS))
7 Likes

Another one-liner to define a very simple hygienic threading macro:

(define-syntax-parse-rule (with it:id body:expr ...)
  (let* ([it body] ...) it))

Usage example:

(with it "Hello World"
      (string-downcase it)
      (string-split it)
      (map string->list it)
      (displayln it))
; displays: ((h e l l o) (w o r l d))
7 Likes

I haven't seen this before... the other threading macros I've used shade gently into point-free mania, this one seems more intuitive. Many thanks!

2 Likes

Not the fanciest ones I've come up with, but maybe my most used: macros to make defining parameters a bit more convienent:

#lang racket/base

(require (only-in srfi/235 [boolean make-boolean])
         (for-syntax racket/base syntax/parse))

(provide define-parameter define-boolean-parameter)

; (define-parameter foo 1234) etc
(define-syntax (define-parameter stx)
  (syntax-parse stx
    ((define-parameter name:id initial-value:expr)
     #'(define name (make-parameter initial-value #f (quote name))))
    ((define-parameter name:id initial-value:expr guard:expr)
     #'(define name (make-parameter initial-value guard (quote name))))
    ((define-parameter name:id initial-value:expr guard:expr obj-name:id)
     #'(define name (make-parameter initial-value guard obj-name)))))

; Parameters that always return #t or #f
(define-syntax (define-boolean-parameter stx)
  (syntax-parse stx
    ((define-boolean-parameter name:id)
     #'(define-parameter name #t make-boolean))
    ((define-boolean-parameter name:id initial-value:boolean)
     #'(define-parameter name initial-value make-boolean))
    ((define-boolean-parameter name:id initial-value:boolean obj-name:id)
     #'(define-parameter name initial-value make-boolean obj-name))))

(Part of my soup-lib package)

2 Likes

Another one I've been playing with, a version of case that uses predicates instead of list membership. Demonstrates using custom syntax classes to break the pattern matching up into manageable pieces, and some more complicated syntax-parse patterns involving alternatives and optional matches:

(define-syntax (case-when stx)
  (define-syntax-class clause
    #:literals (=>)
    (pattern (~or* (pred:expr => func:expr) (pred:expr body:expr ...+))
      #:fail-when (free-identifier=? #'pred #'else) #f))
  (define-syntax-class else-clause
    #:literals (else =>)
    (pattern (~or* (else => func:expr) (else body:expr ...+))))
  (syntax-parse stx
    [(_ val:expr cl:clause ...+ (~optional else-cl:else-clause))
     #'(let ([obj val])
         (cond
           [(cl.pred obj) (~? (cl.func obj) (~@ cl.body ...))] ...
           (~? (else (else-cl.func obj)) (~? (else else-cl.body ...)))))]))

Sample usage:

(case-when "abcd"
  [integer? 'integer]
  [symbol? 'symbol]
  [else 'other])
1 Like