使var跨阶段可访问

时间:2018-12-17 05:02:45

标签: racket

如何在多个阶段中访问相同的变量?例如:

foo 会抱怨{{1}}在阶段0中未定义。

1 个答案:

答案 0 :(得分:4)

这取决于您希望此“变量”用于什么。以下是一些选项:

  1. 您希望它是一个不变的原始值。
  2. 您希望它是不可变的,但是像结构(而不​​是原始)这样的复杂数据。
  3. 您希望它是可变的

在莱夫·安徒生(Leif Andersen)的评论中,她说“你不可能,即使可以也不能”。我猜她在说(3)。 Racket编译器试图使“内部” Racket运行时系统中的(3)变为不可能。在球拍文档中,这称为The Separate Compilation GuaranteeThe Separate Compilation Guarantee背后有很好的理由,以及为什么(3)是个坏主意,莱夫·安徒生(Leif Andersen)或马修·弗拉特(Matthew Flatt)可以比我更好地解释这些理由。论文Composable and Compilable Macros: You Want it When?和演讲Racket: Metaprogramming Time!解释了其中一些原因。

但是,如果不可变的值足够了,我将尝试显示(1)和(2)的答案。

1:不可变的原始数据

由于数据是不可变的,因此可以在不同的阶段使用“两个不同的标识符”。如果它们以相同的值开头,并且没有一个被突变,那么它们将保持相同的值。

(begin-for-syntax
  (define foo 1))
(define foo 1)

但是,这不能保证两个foo变量具有相同的值,即使它们看起来相同。 “用于语法”的环境和正常环境可能会有所不同,并以不同的方式解释这些表达式。为了确保不会发生这种情况,最好在模块中使用foo单个定义。这样可以确保它始终具有相同的一致环境。然后,您可以针对“语法”和正常方式多次要求该模块:

(module foo racket
  (provide foo)
  (define foo 1))

(require (for-syntax 'foo)
         'foo)

2:不变的复杂数据

有两种方法可以将其扩展到诸如struct之类的复杂数据:

2a:不变,复杂的数据,预制结构

通常结构是“生成的”,这意味着在两个不同地方写的看起来相同的定义将生成两种不同的结构类型,而这些结构将永远不相等

但是,带有#:prefab的结构是特殊的,因为如果在两个不同的位置具有相同的定义,即使在“语法”和普通级别上,它们的确会产生相等的值。

(begin-for-syntax
  (struct rgb-color [r g b] #:prefab)
  (define red (rgb-color 255 0 0)))
(struct rgb-color [r g b] #:prefab)
(define red (rgb-color 255 0 0))

或者您也可以将其放在模块中,并在语法和常规级别都需要它:

(module red racket
  (provide red)
  (struct rgb-color [r g b] #:prefab)
  (define red (rgb-color 255 0 0)))

(require (for-syntax 'red)
         'red)

2b:不可变,复杂的数据,跨相持久结构

如果prefab结构还不够好,并且您希望结构看起来不像“任何东西”原始数据,而更像抽象,那么可以在模块中定义结构专门标记为cross-phase-persistent

但是,这不像编写struct那样简单。 cross-phase-persistent的语法非常严格。据我所知,您将不得不低级使用并且仅使用#%kernelmake-struct-type之类的define-values形式。您的结构声明可能必须如下所示:

#lang racket/kernel
(#%declare #:cross-phase-persistent)

(define-values [_1 rgb-color rgb-color? rgb-color-ref _5]
  (#%app make-struct-type 'rgb-color '#f '3 '0 '#f '() 'prefab '#f
         (#%app list '0 '1 '2)))

这是一个更大的方法,因此更常见的解决方案是仅使用prefab结构。