tutorial. Scheme解释器的闭包与环境[lp-0008]

(flora metacircular-runtime)导出application-fragment与environment-fragment两个片段值。Scheme解释器通过use-modules导入它们,计划显式实例化时才建立lookup与apply-value的函数binding。transclude独立复用这些定义的阅读内容,不进行程序导入。

Scheme闭包应用与map/apply原语[#55]

program. Scheme 闭包应用 apply-value 的定义[mce-apply]

Defines: (apply-value map-values all-lists? all-empty? any-empty?)

Assembled in Scheme 解释器的装配计划, Scheme求值与闭包应用模块

Outputs: metacircular.scm, lp-example/mce/engine.scm

Requires: evaluate-sequence, extend-environment

(define (apply-value procedure arguments)
       (cond
         ((and (vector? procedure) (eq? (vector-ref procedure 0) 'primitive))
          (let ((implementation (vector-ref procedure 1)))
            (cond ((eq? implementation 'map)
                   (map-values (car arguments) (cdr arguments)))
                  ((eq? implementation 'apply)
                   (unless (= (length arguments) 2) (throw 'mce-arity 'apply arguments))
                   (apply-value (car arguments) (cadr arguments)))
                  (else (apply implementation arguments)))))
         ((and (vector? procedure) (eq? (vector-ref procedure 0) 'closure))
          (evaluate-sequence (vector-ref procedure 2)
            (extend-environment (vector-ref procedure 1) arguments
                                (vector-ref procedure 3))))
         (else (throw 'mce-not-procedure procedure))))

(define (map-values procedure lists)
       (unless (and (pair? lists) (all-lists? lists)) (throw 'mce-arity 'map lists))
       (if (null? (car lists))
           (if (all-empty? lists) '() (throw 'mce-arity 'map lists))
           (begin
             (when (any-empty? lists) (throw 'mce-arity 'map lists))
             (let ((value (apply-value procedure (map car lists))))
               (cons value (map-values procedure (map cdr lists)))))))

(define (all-lists? xs)
       (or (null? xs) (and (list? (car xs)) (all-lists? (cdr xs)))))

(define (all-empty? xs)
       (or (null? xs) (and (null? (car xs)) (all-empty? (cdr xs)))))

(define (any-empty? xs)
       (and (pair? xs) (or (null? (car xs)) (any-empty? (cdr xs)))))

Scheme解释器将闭包表示为带有参数、正文和定义环境的向量。apply-value区分宿主原语与对象语言闭包,闭包应用需要evaluate-sequence和extend-environment,片段的Requires显示这两个依赖。map与apply必须经过解释器自己的应用机制,才能接受对象语言闭包;直接借用宿主map会把闭包向量错误地当作宿主过程。当前apply支持过程与一个参数列表,map支持等长列表。

Scheme递归定义的共享环境框架[#56]

program. Scheme 环境查找、扩展与 define 的实现[mce-env]

Defines: (extend-environment lookup define-value!)

Assembled in Scheme 解释器的装配计划, Scheme环境模块

Outputs: metacircular.scm, lp-example/mce/environment.scm

(define (extend-environment names values outer)
       (unless (= (length names) (length values)) (throw 'mce-arity names values))
       (cons (vector (map cons names values)) outer))

(define (lookup name environment)
       (if (null? environment) (throw 'mce-unbound name)
           (let ((binding (assq name (vector-ref (car environment) 0))))
             (if binding (cdr binding) (lookup name (cdr environment))))))

(define (define-value! name value environment)
       (let* ((frame (car environment)) (binding (assq name (vector-ref frame 0))))
         (if binding (set-cdr! binding value)
             (vector-set! frame 0 (cons (cons name value) (vector-ref frame 0))))))

Scheme用户环境是框架链,每个框架保存可变的名字与值关联。闭包捕获框架链,define更新同一个框架,因此递归过程能够查找到自己的binding。框架内的binding使用dotted pair;环境实现保存原文、位置和片段身份,装配到新模块时不继承本文章的词法作用域。

片段值的查询接口来自(lp core):fragment?、fragment-id、fragment-heading、fragment-origin、fragment-requires与fragment-datums。记录不提供setter;其中的语法与datum仍应当作为只读值使用。requires是作者声明的依赖,不是自动推断的完整自由变量集合。装配检查依赖是否来自同一装配或宿主默认接口。

Scheme运行时片段的跨文章导入[#57]

声明模块并导出片段值,与直接导出解释器函数不同:

@(define-module (flora metacircular-runtime)
  #:use-module (lp core)
  #:use-module (lp scribble)
  #:export (application-fragment environment-fragment))

消费者通过(flora metacircular-runtime)导入这两个值,将它们加入make-program的清单。实例化会在共同命名空间建立函数。片段tree在来源文章只展示代码与Requires;所属计划的链接由消费者的Module tree建立,来源关系可在Backlinks中查阅。