Optional Scribble Sections; General Macro for Inclusion / Exclusion of Code

Hi, Racket Discourse.

I am wondering whether there is an "obvious" way to include or exclude certain pieces of code based on some form of "check". In particular, I want to exclude and include certain sections in a Scribble document based on the presence or absence of certain values in the results of an API query.

Basically, certain events reported on in the results necessitate the addition of optional sections, which are perhaps absent again for other results, although the report itself is the same general document; think integrations being present or not, that kind of thing.

In essence, this isn't particularly difficult. Using something like a JSON object (stored in a file) to check the inclusion/exclusion cases can be done quite easily, a stupid example would be:

(require (for-syntax racket/match
                     syntax/parse
                     json))

(begin-for-syntax
  (define (check-json key val filepath)
    (match (with-input-from-file filepath
             (lambda () (read-json)))
      [(hash-table ((== key) (== val))) #t]
      [_                                #f])))

(define-syntax (with-inclusion-condition stx)
  (syntax-parse stx
    [(with-inclusion-condition #t
       BODY ...)
     #'(begin
         BODY ...)]
    [(with-inclusion-condition #f
       BODY ...)
     #'(void)]
    [(with-inclusion-condition (KEY VAL FILEPATH)
       BODY ...)
     (with-syntax ([result (check-json (syntax->datum #'KEY) (syntax->datum #'VAL)
                                       (syntax->datum #'FILEPATH))])
       #'(with-inclusion-condition result BODY ...))]))

Of course, this becomes more complicated if one were to allow arbitrary code to be executed as the check. I can't just use eval-syntax willy-nilly, because the necessary external pieces of code might not be present at the time or place where the check is performed.

Would this be considered "un-Rackety", so to speak, because of a better/more general solution?

1 Like

Here is an example:

#lang scribble/base
@(require racket/format)
@(define api-result #t)

@title{Foo Report}

@(if api-result
     @~a{The result was positive.}
     @~a{The result was negative.})
1 Like

Hi, @soegaard, thanks for the quick reply.

Would this work if the branches of the conditional expression included entire sections, though? Something like this fails.

@(if #t
     (begin
       (section #:tag "example-true" "True Example")
       (subsection #:tag "example-sub-true" "True Sub Example"))
     (begin
       (section #:tag "example-false" "False Example")
       (subsection #:tag "example-sub-false" "False Sub Example")))

The reason I resorted to macros is because I figured the section had to be "spliced" in the larger document, as opposed to what happens above.

Yes, if you use list instead of begin:

@(if #t
     (list
       (section #:tag "example-true" "True Example")
       (subsection #:tag "example-sub-true" "True Sub Example"))
     (list
       (section #:tag "example-false" "False Example")
       (subsection #:tag "example-sub-false" "False Sub Example")))

Tip: You can run the Scribble program and then enter doc in the repl to see the
datastructure generated by decode.

3 Likes

What? Mind blown :exploding_head:, haha. Thank you, @soegaard.

1 Like

I do something similar in the Racket glossary statistics script. :slight_smile:

I require the Scribble file as a module, which on the fly updates a hash according to properties of the glossary entries. The script then accesses this statistics data and formats it as a textual bar chart.

2 Likes

I just want to reiterate what an eye-opening comment this was. I'm busy mostly with documentation, lately, and knowing this has made a lot of problems a lot easier to solve.

2 Likes