💾 Archived View for gem.librehacker.com › gemlog › starlog › 20240122-0.gmi captured on 2024-08-31 at 12:13:47. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2024-02-05)

-=-=-=-=-=-=-

Introspection of Guile Scheme Modules (publ. 2024-01-22)

One of the most fun things about working with lisps is the (often) great support for introspection. That means looking inside objects, in real time, to find out what they are, what they are made of, and how they work.

In Common Lisp this is really fun, because the built-in inspector provides lots of information, and we also have Emacs SLIME, which makes inspection navigation as easy as a stroll in the garden. Included in that is the source code location and, if you dive deep enough down the tree, the underlying assembly code.

CL is my lisp of choice now, but lately I've been trying to become more intimate with Guix REPL, which is just a Guile REPL and a bunch of scheme modules. So I've been trying to figure out Guile introspection. The Guile REPL has a built-in introspection tool, but it usually doesn't show you much more than the type name of the object. But inspection procedures are available. This is from Guile version 3.0.9:

Looking at a module

If you don't already know what exact procedures you are looking for, it is helpful to inspect modules. First, you need to get a module object. If you are already in the module of interest, you can run

scheme@(guix-user)> ,m guix
scheme@(guix)> (current-module)
$7 = #<directory (guix) 7fd5b92bfe60>

But it is more convenient usually to get the module from outside of it, using `resolve-module':

scheme@(guile-user)> (resolve-module '(guix))
$8 = #<directory (guix) 7fd15472f960>

To look over defined symbols in the module, there is `module-for-each', which iterates over each symbol/value pair. You can list out the symbol names with something like this:

(define (list-module-symbols module)
  (module-for-each (lambda (a b) (map display (list a "\n"))) module))

E.g.:

scheme@(guile-user)> (list-module-symbols (resolve-module '(guix packages)))
lookup-package-propagated-input
package-build-system
package-location-vector
origin-patch-flags
transitive-inputs
%content-hash?-procedure
...etc...

Finding the source

For procedures, you can (usually) get the location of the source code with `program-sources'. First you need to be able to get the procedure object, either from within the module, or from reaching into it with `@' or `@@'.

scheme@(guile-user)> ,use (system vm program)
scheme@(guile-user)> (@@ (guix packages) package-build-system)
$12 = #<procedure %package-build-system-procedure (s)>
scheme@(guile-user)> (program-sources $12)
$13 = ((0 "guix/packages.scm" 567 . 0))

Section 6.7.3 of the Guile manual describes the accessors:

-- Scheme Procedure: source:addr source
-- Scheme Procedure: source:line source
-- Scheme Procedure: source:column source
-- Scheme Procedure: source:file source

There can be more than one source datum, so you would need to use `car' to get the first one:

scheme@(guile-user)> (source:file (car (program-sources (@@ (guix packages) package-build-system))))
$15 = "guix/packages.scm"

Lunch break is over, so I'll have to end it here for today...