Это не отвечает на ваш первоначальный вопрос, это ответ на ваш комментарий о создании собственного. Я думал, что это действительно интересная идея, поэтому я ее изучил. То, что я был в состоянии понять:
Допустим, вы хотите, чтобы это работало:
(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, например +
, вы не можете получить доступ к e ажа переменного верхнего уровня, как top-x
:
> (f)
0
1
2
PRY> top-x
; top-x: undefined;
; cannot reference undefined identifier
Вы можете получить материал верхнего уровня, давая eval
доступ к текущему пространству имен, как описано here.Так 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))))
И получить тот аргумент, что нужно это колдовство в файл debugee:
(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)))) ; <---
Теперь РЕПЛ можно увидеть и изменить 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
Shoot. Причина объясняется here.
Вы можете себе представить, что даже если Eval не может видеть локальные привязки в разорванной Eval-формуле, должно быть на самом деле структура данных, отображение х 2, а у 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
Так что я думаю, что бы точка трамплин, если вы хотите, чтобы решить это.
Я думаю, что это отличный вопрос. Приехав в Racket несколько лет назад, я смутился, что вряд ли кто-нибудь использовал отладчик, как я был религиозным в использовании с C++. Оказалось, это не имеет большого значения. 1. Я использую случайные «log-debug» или «trace». 2. Я играю с небольшими функциями в REPL, настраивая и наблюдая за ними. 3. Функции в основном «функциональные» (не полагайтесь на чтение или запись внешнего состояния). Со всеми этими частями я почти никогда не хотел традиционного отладчика снова. Сказав все это, что-то вроде «pry» звучит интригующе. –