Возможность проверять определения функций и макросов - это особенность вашей среды разработки. В наши дни типично использовать SLIME или SLY с emacs в качестве основы среды разработки Lisp. Я лично использую SLIME, но я тоже слышал хорошие отзывы о SLY.
В SLIME вы можете вызвать slime-edit-definition
(либо набрав M-x slime-edit-definition
, либо используя комбинацию клавиш M-.
), чтобы посетить определение для символа под курсором в исходном файле. Это работает как при редактировании в исходном файле, так и в REPL. Эта функция чрезвычайно полезна, когда вы хотите проверить код некоторой библиотеки, с которой вы работаете, но вы также можете таким образом просмотреть множество встроенных определений. Вы даже можете перейти к новому определению из нового символа, найденного в любом определении, которое вы в настоящее время просматриваете.
После того, как вы закончите просмотр определения, вы можете использовать M-x slime-pop-find-definition-stack
или более простую для запоминания привязку клавиш M-,
(M-*
также будет работать), чтобы вернуться к ранее просмотренным определениям, в конечном итоге вернувшись к исходной точке.
Вот пример в SBCL:
CL-USER> with-open-file[press M-.]
(Обратите внимание, что «[нажмите M-.]» Выше не набирается, а только для напоминания, какие действия здесь предпринимаются). Поместив курсор на символ with-open-file
или сразу после него, нажмите M-.
, чтобы увидеть определение:
(sb-xc:defmacro with-open-file ((stream filespec &rest options)
&body body)
(multiple-value-bind (forms decls) (parse-body body nil)
(let ((abortp (gensym)))
`(let ((,stream (open ,filespec ,@options))
(,abortp t))
,@decls
(unwind-protect
(multiple-value-prog1
(progn ,@forms)
(setq ,abortp nil))
(when ,stream
(close ,stream :abort ,abortp)))))))
На этот раз после нажатия M-.
SLIME дает возможность выбора определений для просмотра:
CL-USER> and[press M-.]
Отображается в буфере emacs:
/path-to-source/sbcl-2.0.4/src/code/macros.lisp
(DEFMACRO AND)
/path-to-source/sbcl-2.0.4/src/pcl/ctypes.lisp
(DEFINE-METHOD-COMBINATION AND)
Мы хотим увидеть определение макроса, поэтому переместите курсор в строку, показывающую (DEFMACRO AND)
, и отобразится следующее определение:
;; AND and OR are defined in terms of IF.
(sb-xc:defmacro and (&rest forms)
(named-let expand-forms ((nested nil) (forms forms) (ignore-last nil))
(cond ((endp forms) t)
((endp (rest forms))
(let ((car (car forms)))
(cond (nested
car)
(t
;; Preserve non-toplevelness of the form!
`(the t ,car)))))
((and ignore-last
(endp (cddr forms)))
(car forms))
;; Better code that way, since the result will only have two
;; values, NIL or the last form, and the precedeing tests
;; will only be used for jumps
((and (not nested) (cddr forms))
`(if ,(expand-forms t forms t)
,@(last forms)))
(t
`(if ,(first forms)
,(expand-forms t (rest forms) ignore-last))))))
Здесь есть еще кое-что, так как теперь вы фактически находитесь в исходном файле, который содержит определение для and
; если вы немного прокрутите вниз, вы также можете найти определение для or
.
Многие функции SBCL написаны на Lisp; SBCL имеет очень качественный компилятор, поэтому многие вещи, которые в противном случае можно было бы ожидать написания на C, могут быть написаны на Lisp без потери производительности. Вот определение функции list-length
:
CL-USER> list-length[press M-.]
(defun list-length (list)
"Return the length of the given List, or Nil if the List is circular."
(do ((n 0 (+ n 2))
(y list (cddr y))
(z list (cdr z)))
(())
(declare (type fixnum n)
(type list y z))
(when (endp y) (return n))
(when (endp (cdr y)) (return (+ n 1)))
(when (and (eq y z) (> n 0)) (return nil))))
То же самое можно сделать при использовании CLISP с SLIME. Вот with-open-file
, как определено в CLISP:
CL-USER> with-open-file[press M-.]
(defmacro with-open-file ((stream &rest options) &body body)
(multiple-value-bind (body-rest declarations) (SYSTEM::PARSE-BODY body)
`(LET ((,stream (OPEN ,@options)))
(DECLARE (READ-ONLY ,stream) ,@declarations)
(UNWIND-PROTECT
(MULTIPLE-VALUE-PROG1
(PROGN ,@body-rest)
;; Why do we do a first CLOSE invocation inside the protected form?
;; For reliability: Because the stream may be a buffered file stream,
;; therefore (CLOSE ,stream) may produce a disk-full error while
;; writing the last block of the file. In this case, we need to erase
;; the file again, through a (CLOSE ,stream :ABORT T) invocation.
(WHEN ,stream (CLOSE ,stream)))
(WHEN ,stream (CLOSE ,stream :ABORT T))))))
Но многие функции CLISP написаны в C, и эти определения недоступны для проверки так же, как раньше:
CL-USER> list-length[press M-.]
No known definition for: list-length (in COMMON-LISP-USER)