如何在全局宏define-syntax中用局部宏let-syntax实现声明式图结构
声明式图结构构建的宏卫生问题与解决方案
需求与已有定义
作为Scheme初学者,希望通过宏实现声明式的图结构构建,已定义了:graph和:node等记录类型:
(define-record-type :graph (make-graph nodes links) graph? (nodes graph-nodes) (links node-links)) (define-record-type :node (make-node name pins) node? (name node-name) (pins node-pins))
其中:
pin是字符串类型的引脚名称link是endpoint的序对,endpoint是节点名与引脚名的序对
期望通过graph、node、link宏支持如下声明式定义:
(define g (graph (node "v0" (pin "out")) (node "v1" (pin "out")) (node "add" (pin "lhs") (pin "rhs") (pin "out")) (link (("v0" . "out") . ("add" . "lhs"))) (link (("v1" . "out") . ("add" . "rhs"))))
要求:
- 生成的结果为
:graph类型值,(graph-nodes g)返回3个:node实例的列表,(graph-links g)返回对应连接列表 - 仅
graph宏在全局作用域定义,node、pin等宏不能置于全局,避免命名冲突 - 宏需提供灵活的声明式风格:无需记忆参数顺序、可混合节点/连接声明顺序、扩展图记录不破坏现有定义代码
无效的嵌套宏尝试
最初尝试通过全局graph宏内定义局部node宏的方式实现,但无法正常工作:
(define-syntax graph (syntax-rules () ((graph . body) (let ((nodes '') (links '')) (let-syntax ((node (syntax-rules () ((node name)) (make-node name '')))) (begin . body)) (make-graph nodes links))))) (graph (node "v0") (node "v1"))
问题原因:Scheme的宏卫生机制会对局部定义的node宏进行重命名,导致body中的node无法被识别,出现未绑定错误。
基于syntax-case的可行实现
通过syntax-case实现了符合需求的方案,代码如下(补充了:link和:pin记录类型以完善结构):
(define-record-type :graph (make-graph nodes links) graph? (nodes graph-nodes) (links graph-links)) (define-record-type :node (make-node name pins) node? (name node-name) (pins node-pins)) (define-record-type :link (make-link from to) link? (from link-from) (to link-to)) (define-record-type :pin (make-pin name) pin? (name pin-name)) (define-syntax graph (lambda (x) (define node-def? (lambda (def) (eq? (car def) 'node))) (define link-def? (lambda (def) (eq? (car def) 'link))) (define pin-def? (lambda (def) (eq? (car def) 'pin))) (define (parse-pin-definition pin-def) #`(make-pin (quote #,(cdr pin-def)))) (define (parse-node-definition node-def) (let ((pins (map parse-pin-definition (filter pin-def? (cddr node-def))))) #`(make-node (quote #,(cadr node-def)) (list #,@pins)))) (define (parse-link-definition link-def) #`(make-link (quote #,(cadr link-def)) (quote #,(cddr link-def)))) (define (parse-graph-definition graph-def) (let ((nodes '') (links '')) (map (lambda (def) (cond ((node-def? def) (set! nodes (cons (parse-node-definition def) nodes)))) (cond ((link-def? def) (set! links (cons (parse-link-definition def) links))))) graph-def) (values nodes links))) (syntax-case x () ((graph . graph-def) (let-values (((nodes links) (parse-graph-definition (syntax->datum #'graph-def)))) #`(make-graph (list #,@nodes) (list #,@links))))))) (display (graph (node "v0" (pin "out")) (node "v1" (pin "out")) (node "add" (pin "lhs") (pin "rhs") (pin "sum")) (link ("v0" . "out") ("add" . "lhs")) (link ("v1" . "out") ("add" . "rhs"))))
该实现通过在宏展开阶段直接解析graph体内的node、pin、link定义,避免了宏卫生问题,同时满足了仅全局暴露graph宏的要求。代码虽缺少错误处理,但清晰展示了syntax-case的使用方式,能正常生成预期的图结构实例。
内容的提问来源于stack exchange,提问作者Wouter Bijlsma
相关产品推荐
相关产品推荐

