这不是在回答您最初的问题,而是在回应您关于制作自己的问题的评论。我认为这是一个非常有趣的想法,所以我进行了探索。我能够弄清楚:
假设您希望它起作用:
(define top-x 10)
(define (f)
(for ([i 10])
(displayln i)
(when (= i 5)
(pry)))) ; <= drop into a REPL here, resume after exiting REPL
第一次尝试pry
:
(define (pry)
(let loop ()
(display "PRY> ")
(define x (read))
(unless (or (eof-object? x) (equal? x '(unquote exit)))
(pretty-print (eval x))
(loop))))
这似乎有效:
> (f)
0
1
2
PRY> (+ 10 10)
20
PRY> ,exit
3
4
>
但是,尽管它可以让您访问 Racket 之类的函数+
,但您甚至无法访问您的顶级变量,例如top-x
:
> (f)
0
1
2
PRY> top-x
; top-x: undefined;
; cannot reference undefined identifier
eval
您可以通过授予对当前命名空间的访问权限来获取顶级内容,如此处所述。所以pry
需要一个命名空间参数:
(define (pry ns)
(let loop ()
(display "PRY> ")
(define x (read))
(unless (or (eof-object? x) (equal? x '(unquote exit)))
(pretty-print (eval x ns)) ; <---
(loop))))
为了得到这个参数,你需要这个咒语到你的被调试文件:
(define-namespace-anchor a) ; <---
(define ns (namespace-anchor->namespace a)) ; <---
(define top-x 10)
(define (f)
(for ([i 5])
(displayln i)
(when (= i 2)
(pry ns)))) ; <---
现在 REPL 可以看到并改变top-x
:
> (f)
0
1
2
PRY> top-x
10
PRY> (set! top-x 20)
#<void>
PRY> top-x
20
PRY> ,exit
3
4
>
凉爽的!但它不能改变局部变量,i
:
> (f)
0
1
2
PRY> i
; i: undefined;
; cannot reference an identifier before its definition
射击。原因在这里解释。
您可能会想象,即使 eval 无法看到损坏的 eval-formula 中的本地绑定,但实际上必须有一个数据结构将 x 映射到 2 并将 y 映射到 3,并且您想要一种获取该数据结构的方法。事实上,不存在这样的数据结构;编译器可以在编译时自由地将 x 的每次使用替换为 2,因此 x 的本地绑定在运行时不存在任何具体意义。即使不能通过常量折叠消除变量,通常也可以消除变量的名称,并且保存局部值的数据结构不像从名称到值的映射。
你可能会说,好吧,但在那种情况下......
DrRacket 如何提供调试器?
据我所知,DrRacket 通过在评估程序之前注释语法来做到这一点。从drracket/gui-debugger/annotator.rkt:
;; annotate-stx inserts annotations around each expression that introduces a
;; new scope: let, lambda, and function calls. These annotations reify the
;; call stack, and allows to list the current variable in scope, look up
;; their value, as well as change their value. The reified stack is accessed
;; via the CURRENT-CONTINUATION-MARKS using the key DEBUG-KEY
所以我认为如果你想解决这个问题,这将是一个起点。