Scheme 如何使用当前作用域打开racket REPL?

Scheme 如何使用当前作用域打开racket REPL?,scheme,racket,Scheme,Racket,假设我有一个这样的程序: (define (foo x) (local ((define y (- x 1))) (* x y))) (foo 3) 我希望能够在第3行和第4行之间打开一个REPL,这样我就可以通过执行任意语句来探索(并可能修改)x和y的值 要在Ruby中实现这一点,我将使用等效的程序: def foo(x) lambda { y = x - 1 x * y }.call end put

假设我有一个这样的程序:

(define (foo x) 
  (local 
    ((define y (- x 1)))
    (* x y)))
(foo 3)
我希望能够在第3行和第4行之间打开一个REPL,这样我就可以通过执行任意语句来探索(并可能修改)x和y的值

要在Ruby中实现这一点,我将使用等效的程序:

def foo(x)   
  lambda {   
    y = x - 1
    x * y    
  }.call     
end       
puts (foo 3)
并通过添加对pry的调用对其进行修改,以在需要的地方提供一个范围很好的repl:

require 'pry'
def foo(x)   
  lambda {   
    y = x - 1
    binding.pry
    x * y    
  }.call     
end       
puts (foo 3)
为了在js中实现这一点,我将在Firebug下运行这个程序,并在第4行设置一个断点:

foo = function(x) {  
  return (function(){
    var y = x - 1;   
    return x * y;    
  })();              
};                                    
console.log(foo(3)); 
然后,我可以在评估窗口中浏览内容


我能做些什么把这个放到球拍里吗?我找到的最接近的是DrScheme的调试器,但它只显示了当前作用域的所有值,据我所知,它不允许您在REPL中探索它们。

在DrRacked IDE中,您有一个DEBUG Q>|按钮。您可以单步执行您的程序,也可以按其他语言中所说的操作,在要研究的表达式处按鼠标右键,然后选择“继续到此点”仅一次,或者选择“暂停到此点”以获得断点,然后按“转到>运行程序”

要检查或更改
x
,请将鼠标指针放在其上并使用鼠标右键。要更改,请在菜单中选择
(设置!x…


至于in-language repl,您可以在那里创建自己的
(pry)
来启动repl,而在Common Lisp中,您可以发出错误信号以访问nice调试器。

这不是回答您最初的问题,而是回应您关于创建自己的调试器的评论。我认为这是一个非常有趣的想法,所以我对它进行了探索。我能想到的是:

比如说,你想让它起作用:

(define top-x 10)
(define (f)
  (for ([i 10])
    (displayln i)
    (when (= i 5)
      (pry)))) ; <= drop into a REPL here, resume after exiting REPL
> (f)
0
1
2
PRY> (+ 10 10)
20
PRY> ,exit
3
4
> 
这似乎有效:

(define top-x 10)
(define (f)
  (for ([i 10])
    (displayln i)
    (when (= i 5)
      (pry)))) ; <= drop into a REPL here, resume after exiting REPL
> (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
> (f)
0
1
2
PRY> top-x
10
PRY> (set! top-x 20)
#<void>
PRY> top-x
20
PRY> ,exit
3
4
> 
如前所述,您可以通过提供对当前名称空间的
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))))
酷!但是它不能改变局部变量,
i

> (f)
0
1
2
PRY> i
; i: undefined;
; cannot reference an identifier before its definition
开枪。解释了原因

您可能会想象,即使eval无法在破损的eval公式中看到本地绑定,但实际上必须有一个数据结构将x映射到2,y映射到3,并且您需要一种获得该数据结构的方法。事实上,不存在这样的数据结构;编译器可以在编译时自由地将x的每次使用替换为2,这样x的本地绑定在运行时就不存在了。即使不能通过常数折叠消除变量,通常也可以消除变量的名称,并且保存局部值的数据结构不类似于从名称到值的映射

你可能会说,好吧,但是那样的话

DrRacket如何提供调试器? 据我所知,DrRacket是通过在评估程序之前注释语法来实现的。发件人:


因此,如果你想解决这个问题,我认为这将是一个起点。

我认为这是一个很好的问题。几年前我来到了球拍网,我很困惑,几乎没有人使用像我一样的使用C++的调试器。结果证明没那么重要。1.我偶尔使用
日志调试
跟踪
。2.我在REPL中玩一些小功能,调整并观察它们。3.这些函数大多是“功能性的”(不依赖于读取或写入外部状态)。有了这些东西,我几乎再也不需要传统的调试器了。说了这么多,像
pry
这样的东西听起来确实很有趣。谢谢-我知道这些调试器功能。我特别想要REPL,因为这是我最喜欢的调试方式——特别是当
x
可能是一个复杂的值,并且只需将其打印到屏幕上就可以了。很高兴听到我可能只是想做我自己的窥探。如果它还没有内置的话,我正在考虑这样做。一个有趣的练习-可能是call/cc的工作…Greg,我终于有时间跟进你的建议了,我遇到了一个障碍,无法确定我可以传递给annotate函数的确切语法。如果您感兴趣,请查看以下问题:
  ;; 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