Skip to content

Instantly share code, notes, and snippets.

@wilbowma
Created April 15, 2024 23:47

Revisions

  1. wilbowma created this gist Apr 15, 2024.
    197 changes: 197 additions & 0 deletions meow.rkt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,197 @@
    #lang racket

    ;; A well typed macro that manipulates binding (in a trivial way)
    (module A racket
    (require
    (for-syntax
    racket/function
    syntax/parse
    racket/syntax
    syntax/transformer))

    (define-syntax (elab-e e)
    (syntax-parse e
    [(_ (lambda (x ...) body))
    #:with (new-x ...) (map (curry format-id e "~a") (syntax->list #'(x ...)))
    #`(lambda (new-x ...)
    (let-syntax ([x (make-variable-like-transformer #'new-x)]
    ...)
    body))]))

    (require rackunit)
    (check-equal?
    ((elab-e (lambda (x) x)) 5)
    5)
    (check-equal?
    (((elab-e (lambda (y) (lambda (x) x))) 5) 6)
    6)
    (check-equal?
    (((elab-e (lambda (y) (lambda (x) y))) 5) 6)
    5))

    ;; ill typed: binding error in the generated syntax
    (module B racket
    (require
    racket/function
    syntax/parse
    racket/syntax
    (for-syntax syntax/transformer))

    (define (elab-e e)
    (syntax-parse e
    [(lambda (x ...) body)
    #:with (new-x ...) (map (curry format-id e "~a") (syntax->list #'(x ...)))
    #`(lambda (new-x ...)
    #,(expand
    #`(let-syntax ([x (make-variable-like-transformer #'new-x)]
    ...)
    body)))]))

    (elab-e #'(lambda (x) x))
    (require rackunit)
    (check-exn values
    (thunk (eval (elab-e #'(lambda (x) x)))))
    )

    ;; ill typed: invalid use of an expansion monad operation at run-time, outside the expansion monad
    (module C racket
    (require
    racket/function
    syntax/parse
    racket/syntax
    (for-syntax syntax/transformer))

    (define (elab-e e)
    (syntax-parse e
    [(lambda (x ...) body)
    #:with (new-x ...) (map (curry format-id e "~a") (syntax->list #'(x ...)))
    #`(lambda (new-x ...)
    #,(syntax-local-introduce
    (expand
    #`(let-syntax ([x (make-variable-like-transformer #'new-x)]
    ...)
    body))))]))

    (elab-e #'(lambda (x) x)))

    ;; ill typed: invalid use of an expansion monad operation at run-time, outside the expansion monad
    (module D racket
    (require
    racket/function
    syntax/parse
    racket/syntax
    (for-syntax syntax/transformer))

    (define (elab-e e)
    (syntax-parse e
    [(lambda (x ...) body)
    #:with (new-x ...) (map (curry format-id e "~a") (syntax->list #'(x ...)))
    #`(lambda (new-x ...)
    #,(local-expand
    #`(let-syntax ([x (make-variable-like-transformer #'new-x)]
    ...)
    body)
    'expression
    '()))]))

    (elab-e #'(lambda (x) x)))

    ;; ill typed: phase binding error
    (module E racket
    (require
    racket/function
    syntax/parse
    racket/syntax
    syntax/transformer)

    (define (elab-e e)
    (syntax-parse e
    [(lambda (x ...) body)
    #:with (new-x ...) (map (curry format-id e "~a") (syntax->list #'(x ...)))
    #`(lambda (new-x ...)
    #,(expand #`(let-syntax ([x (make-variable-like-transformer #'new-x)]
    ...)
    body)))]))

    make-variable-like-transformer
    (elab-e #'(lambda (x) x)))

    ;; ill typed: I honestly don't even know, but I know using expand in the
    ;; expansion macro is hard and usually wrong.
    ;; So wrong, and this errors out when the module is visited
    #;(module F racket
    (require
    (for-syntax
    racket/function
    syntax/parse
    racket/syntax
    (for-syntax racket/base syntax/transformer)))

    (define-syntax (elab-e e)
    (syntax-parse e
    [(_ (lambda (x ...) body))
    #:with (new-x ...) (map (curry format-id e "~a") (syntax->list #'(x ...)))
    #`(lambda (new-x ...)
    #,(expand
    #`(let-syntax ([x (make-variable-like-transformer #'new-x)]
    ...)
    body)))]))

    (elab-e (lambda (x) x)))

    ;; ill typed: expanding in an unbound environment
    ;; raises error when module is visited
    #;(module G racket
    (require
    (for-syntax
    racket/function
    syntax/parse
    racket/syntax
    syntax/transformer))

    (define-syntax (elab-e e)
    (syntax-parse e
    [(_ (lambda (x ...) body))
    #:with (new-x ...) (map (curry format-id e "~a") (syntax->list #'(x ...)))
    #`(lambda (new-x ...)
    #,(local-expand
    #`(let-syntax ([x (make-variable-like-transformer #'new-x)]
    ...)
    body)
    'expression
    '()))]))

    (elab-e (lambda (x) x)))

    ;; Well typed version of G
    (module H racket
    (require
    (for-syntax
    racket/function
    syntax/parse
    racket/syntax
    syntax/transformer))

    (define-syntax (elab-e e)
    (syntax-parse e
    [(_ (lambda (x ...) body))
    #:with (new-x ...) (map (curry format-id e "~a") (syntax->list #'(x ...)))
    (define intdef (syntax-local-make-definition-context))
    (define newxs (syntax-local-bind-syntaxes
    (syntax->list #'(new-x ...))
    #f
    (syntax-local-make-definition-context)))
    (with-syntax ([(new-scoped-xs ...) newxs])
    #`(lambda (new-scoped-xs ...)
    #,(local-expand
    #`(let-syntax ([x (make-variable-like-transformer #'new-scoped-xs)]
    ...)
    body)
    'expression
    '()
    intdef)))]))

    (require rackunit)
    (check-equal?
    ((elab-e (lambda (x) x)) 5)
    5))