egallesio / STklos

STklos Scheme
http://stklos.net
GNU General Public License v2.0
68 stars 17 forks source link

Enhancements to `apropos` #601

Closed jpellegrini closed 7 months ago

jpellegrini commented 10 months ago

Hi @egallesio ! I hope I'm not sending too many PRs... :) I had an idea -- the apropos procedure could be enhanced. I have implemented some additional features:

I couldn't actually use :key and :rest, so I implemented this by hand.

And I also included some tests.

Here's the documentation:

(apropos obj :key (internal #f) (imported #f) (exported #t))
(apropos/alist obj :key (internal #f) (imported #f) (exported #t))
(apropos/pp obj :key (internal #f) (imported #f) (exported #t))
(apropos obj :key (internal #f) (imported #f) (exported #t) [ module ...])
(apropos/alist obj :key (internal #f) (imported #f) (exported #t) [ module ...])
(apropos/pp obj :key (internal #f) (imported #f) (exported #t) [ module ...])

These procedures return the symbols whose print name contains the characters of obj as a substring, in the specified modules. The given obj can be a string or symbol, and each module argument can be a symbol, a string, or the module itself.

The keyword arguments are boolean:

The default is to list only the exported symbols.

The three variants of |apropos| are:

Note that using #t will bring the same symbols in several different modules, since modules (as opposed to libraries) inherit all bindings in the stklos module.

Examples:

(define-library a (export (disposition)) (begin (define disposition 10)))

stklos> (apropos/pp 'disp #t)
Library a:
   disposition
Module scheme/write:
   display
Module REPL:
   repl-display-prompt
Module STKLOS-OBJECT:
   display-object
Module STKLOS-COMPILER:
   compiler:time-display
Module SCHEME:
   display
   display-shared
   display-simple
Module stklos:
   %display-backtrace
   compiler:time-display
   display
   display-object
   display-shared
   display-simple
   repl-display-prompt

Also:

(define-library (A)
  (export zeta-one zeta-two zeta-three)
  (begin (define zeta-one 1)
         (define zeta-two 2)
         (define theta-three 3)))
(define-library (B)
  (export zeta-two zeta-three theta-four) ; but not zed
  (begin (define zeta-two 2)
         (define zeta-three 3)
         (define theta-four 4)
         (define zed 5)))
(define-library (C)
  (export zee)
  (import (B))
  (begin (define zee -1)))

(apropos/alist 'zeta
               'A                      ; a symbol (module name)
               (find-module 'B))       ; and a module
         => ( (A (zeta-one zeta-three zeta-two))
              (B (zeta-three zeta-two)) )

(apropos/pp 'zeta
               "A"                     ; a string (module name)
               'B)                     ; a symbol (module name)
         => void
;; And outputs, to the current output port, the following:

Module A:
   zeta-one
   zeta-three
   zeta-two
Module B:
   zeta-three
   zeta-two

(apropos "x" (find-module 'A))         ; obj is a string
         => ()                         ; no symbol found

(apropos 'o '(B))                      ; module name can be a list
         => (theta-four zeta-two)      ; sinple list returned by apropos

(apropos 'o :exported #f '(B))         ; oops, none of the options is active
         => ()                         ; so the result is empty

(apropos 'o :exported #f               ; we suppressed the externals, so
            :internal #t '(B))         ; internals that are exported are out
         => ()                         ; and the result is empty!

(apropos/alist 'a #t)
         => <all symbols exported by all modules with "a" in their names>
egallesio commented 7 months ago

I have applied this PR, but have not retained the :internal, :imported and :exported key parameters. The main reason is that the result is not always accurate. For instance, if a symbol is imported but redefined in a module, it will not be found by the apropos command. This could be avoided since it is possible to know if a symbol is an alias or a locally defined variable. However, it depends heavily on the current implementation of global variables which will probably change in the future.

However, the behavior of apropos depends on the queried module: if the given module is the current one, it returns the symbols defined or imported by the current module. Otherwise, it returns the exported symbols by this module. Hence, apropos tries to return the symbols that can be used from the given module.

There is a place for another function, which tries to find information in modules in an easier way than exploiting directly module introspection functions, but having a separate function for that seems, imo, preferable.

Otherwise, I kept the functions apropos/alist and apropos/pp which is a really good idea.