docstring support?

243 views
Skip to first unread message

Sean McAllister

unread,
Nov 1, 2017, 11:15:05 PM11/1/17
to chez-scheme
I don't think chez has it (at least I couldn't find it).   Would anyone be interested in having it?  I'm particularly interested in being able to program from the repl/emacs and have access to the docstring functionality.   How about a manual that emacs could interface with as well?   I'm not familiar with the code and couldn't find the place where the define special form is created, could anyone point me in the right direction?


Andy Keep

unread,
Dec 10, 2017, 12:49:51 PM12/10/17
to chez-scheme
Define is core syntactic form, partially because it expands differently based on context, becoming a top-level set! when the define is at the top-level of a script file, and part of a letrec* when it is an internal definition (including within a scheme library or program).

It is worth noting though that just because Chez Scheme provides a base define, doesn't mean you cannot create your own define macro, after all there are no reserved words in Scheme.

You could build a R6RS library that exports a define supporting docstrings and a documentation function for looking up the values.  You could do this as a couple of libraries with standard R6RS features based on the implementation for Chicken by Zane Ashby:

docstring-helper.sls which defines a hash table to store document strings in:

(library (docstring-helper)
 
(export docs-ht documentation)
 
(import (rnrs))


 
(define docs-ht (make-eq-hashtable))
 
(define documentation
   
(lambda (name)
     
(hashtable-ref docs-ht name #f))))


docstring.sls which uses the hash table and redefines define:

(library (docstring)
 
(export define documentation)
 
(import (rename (rnrs) (define rnrs:define)) (docstring-helper))


 
(define-syntax define
   
(lambda (x)
     
(syntax-case x ()
       
[(_ (name . fmls) str body0 ... body1)
         
(and (identifier? #'name) (string? (syntax->datum #'str)))
         
(begin
           
(hashtable-set! docs-ht (syntax->datum #'name) (syntax->datum #'str))
           
#'(rnrs:define (name . fmls) body0 ... body1))]
         
[(_ name str expr)
         
(and (identifier? #'name) (string? (syntax->datum #'str)))
         
(begin
           
(hashtable-set! docs-ht (syntax->datum #'name) (syntax->datum #'str))
           
#'(rnrs:define name expr))]
         
[(_ name . rest) #'(rnrs:define name . rest)]))))

This can then be used as follows:

(import (docstring))
(define foo "A constant that represents the answer to the question of the universe" 42)
foo                  
;=> 42
(documentation 'foo)  ;=> "A constant that represents the answer to the question of the universe"

This may not work in some R6RS Scheme implementations, depending on how they handle stateful elements around library phasing, but it will work in Chez Scheme.  You could also do something that attaches the document strings directly to the identifier being defined.  This is potentially useful when different libraries name the same function.  In the hash table version, the docstring would only be preserved for one, but if define-property is used instead of a hash table, the record on the identifiers is kept distinct:

docstring.chezscheme.sls (or you could just name it docstring.ss/docstring.sls if this is the only implementation you have around):

(library (docstring)
 
(export define documentation)
 
(import (rename (chezscheme) (define cs:define)))


 
(cs:define docstring #f) ;; identifier to hang property on


 
(define-syntax define
   
(lambda (x)
     
(syntax-case x ()
       
[(_ (name . fmls) str body0 ... body1)
         
(and (identifier? #'name) (string? (datum str)))
         
#'(begin
             
(cs:define (name . fmls) body0 ... body1)
             
(define-property name docstring str))]
         
[(_ name str expr)
         
(and (identifier? #'name) (string? (datum str)))
         
#'(begin
             
(cs:define name expr)
             
(define-property name docstring str))]
         
[(_ name . rest) #'(cs:define name . rest)])))


 
(define-syntax documentation
   
(lambda (x)
     
(lambda (r) ;; retrieve the syntactic environment
       
(syntax-case x ()
         
[(_ id) (identifier? #'id) (r #'id #'docstring)]
         
[(_ 'id) (identifier? #'id) (r #'id #'docstring)])))))


Then this can be used as follows:

(import (docstring))
(define foo "A constant that represents the answer to the question of the universe" 42)
foo                  
;=> 42
(documentation foo)  ;=> "A constant that represents the answer to the question of the universe"
(documentation 'foo) ;=> "A constant that represents the answer to the question of the universe"
(let ([foo 5]) (write (documentation foo)) (newline) foo)
;=> #f
;=> 5

I've kept the quoted syntax here, even though it is not necessary.  You can see from the later example that if the local identifier differs from the one defined above, it will shadow (but not alter) the original definition.

Hope that helps.

-andy:)
Reply all
Reply to author
Forward
0 new messages