www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

hyper-literate.scrbl (11510B)


      1 #lang scribble/manual
      2 @require[racket/require
      3          @for-label[hyper-literate
      4                     racket/base
      5                     (subtract-in scribble/manual hyper-literate)
      6                     racket/contract]]
      7 
      8 @title{Hyper-literate programming}
      9 @author[@author+email["Suzanne Soy" "racket@suzanne.soy"]]
     10 
     11 @(require scribble/manual
     12           scribble/core
     13           scribble/decode
     14           scribble/racket
     15           (only-in scribble/racket value-link-color))
     16 
     17 @defmodulelang[hyper-literate]
     18 
     19 The @racketmodname[hyper-literate] metalanguage extends the
     20 features of @racketmodname[scribble/lp2], with the goal of
     21 providing a more modern view on literate programming. It can
     22 be parameterized with the language used in the chunks (so
     23 that it is possible to directly write 
     24 @racketmodname[typed/racket] programs with 
     25 @racketmodname[hyper-literate], for example).
     26 
     27 On the first line, which begins with @tt{@litchar{#lang}
     28  @racketmodname[hyper-literate]}, the language recognises the following
     29 options:
     30 
     31 @(require scribble/core
     32           (only-in scribble/private/manual-vars boxed-style)
     33           scribble/private/manual-utils)
     34 @(make-table
     35   boxed-style
     36   (list
     37    (list
     38     @paragraph[(style #f '())]{
     39  @tt{@litchar{#lang} @racketmodname[hyper-literate] @racket[_lang]
     40   @racket[_maybe-no-req] @racket[_maybe-no-auto]}})
     41    flow-empty-line
     42    (list
     43     @racketgrammar*[
     44  (maybe-no-req (code:line)
     45                (code:line #:no-require-lang))
     46  (maybe-no-auto (code:line)
     47                 (code:line #:no-auto-require))])))
     48 
     49 where @racket[_lang] is a module name which can be used as
     50 a @litchar{#lang}, for example @racketmodname[typed/racket]
     51 or @racketmodname[racket/base].
     52 
     53 The current implementation of hyper-literate needs to inject
     54 a @racket[(require _lang)] in the expanded module, in order
     55 to have the arrows properly working in DrRacket for
     56 "built-in" identifiers which are provided by the 
     57 @racket[_lang] itself. The @racket[require] statement is
     58 injected after the whole ``code'' module has been expanded.
     59 It is worth noting that an extra scope is added to the expanded
     60 body of the module, in order to make any @racket[require] form
     61 within more specific than the @racket[(require _lang)].
     62 
     63 The current implementation of @racketmodname[scribble/lp2],
     64 on which @racketmodname[hyper-literate] relies (with a few
     65 changes), extracts the @racket[require] statements from
     66 chunks of code, and passes them to 
     67 @racket[(require (for-label …))]. The goal is to have
     68 identifiers from required modules automatically highlighted
     69 and hyperlinked to their documentation. However, all
     70 meta-levels are smashed into the @racket[#f], i.e. 
     71 @racket[for-label] meta-level. As a consequence, conflicts
     72 can arise at the @racket[for-label] meta-level between two
     73 modules, even if these two modules were originally required
     74 at distinct meta-levels in the source program. It is
     75 possible in this case to disable the feature using 
     76 @racket[#:no-auto-require], and to manually call 
     77 @racket[(require (for-label …))] and handle conflicting
     78 identifiers in a more fine-grained way.
     79 
     80 @deprecated[#:what @racket[#:no-require-lang] ""]{
     81                                                   
     82  The @racket[#:no-require-lang] is deprecated starting from version 0.1, and
     83  is not needed anymore. It is still accepted for backwards compatibility. Note
     84  that version 0.1 of this library requires a fairly recent Racket version to
     85  work properly (it needs v.6.7.0.4 with the commit
     86  @tt{8a7852ebbfeb85a8f860700ba5ae481ed7aa9b41}, or v.6.7.0.5 or later). By
     87  default, raco will install v0.0 of hyper-literate on older Racket versions.
     88 
     89  The extra @racket[require] statement injected by
     90  @racketmodname[hyper-literate] could in previous versions conflict with
     91  user-written @racket[require] statements. These @racket[require] statements
     92  can shadow some built-ins, and this case would yield conflicts. The
     93  @racket[#:no-require-lang] option disables that behaviour in versions < 0.1,
     94  and has the only drawback that built-ins of the @racket[_lang] language do not
     95  have an arrow in DrRacket (but they still should be highlighted with -a
     96  turquoise background when hovered over with the mouse).}
     97 
     98 @section{What is hyper-literate programming?}
     99 
    100 Hyper-literate programming is to literate programming
    101 exactly what hypertext documents are to regular books and
    102 texts. Literate programming is about telling other
    103 programmers how the program works (instead of just telling
    104 the compiler what it does). Telling this story can be done
    105 using non-linear, hyperlinked documents.
    106 
    107 For now these utilities only help with manipulating literate
    108 programming chunks (e.g. repeating the same chunk in several
    109 places in the output document, but keeping a single copy in
    110 the source code).
    111 
    112 Ultimately, the reading experience should be closer to
    113 viewing an interactive presentation, focusing on the parts
    114 of the program that are of interest to you: expand on-screen
    115 the chunks you are curious about, run some tests and see
    116 their result, etc.
    117 
    118 @itemlist[
    119  @item{Imagine something like 
    120   @hyperlink["http://www.andrewbragdon.com/codebubbles_site.asp"]{
    121    code bubbles}, but with explanatory text coming along
    122   with the source code.}
    123  @item{Imagine something like 
    124   @hyperlink["http://inform7.com/"]{Inform}, but focused on
    125   exploring a program instead of exploring an imaginary
    126   world — after all, a program is some kind of imaginary
    127   world.}]
    128 
    129 @section{Chunks of code}
    130 
    131 @; @racket[chunk] does not work for these, probably due to the use of either:
    132 @;   @title[#:tag "lp" …]{Literate Programming}
    133 @; or:
    134 @;   @defmodulelang[scribble/lp2 #:use-sources (scribble/lp)]{…}
    135 @; in scribble-doc/scribblings/scribble/lp.scrbl
    136 @; See scribble bug #51 https://github.com/racket/scribble/issues/51
    137 @(define scribble-chunk
    138    (element symbol-color
    139             (make-link-element value-link-color
    140                                (decode-content (list "chunk"))
    141                                '(form ((lib "scribble/lp.rkt") chunk)))))
    142 @(define scribble-CHUNK
    143    (element symbol-color
    144             (make-link-element value-link-color
    145                                (decode-content (list "CHUNK"))
    146                                '(form ((lib "scribble/lp.rkt") CHUNK)))))
    147 
    148 @;{
    149  @(module scribble-doc-links racket/base
    150     (require scribble/manual
    151              (for-label scribble/lp2
    152                         scribble/private/lp))
    153     (provide (all-defined-out))
    154     (define scribble-chunk @racket[chunk])
    155     (define scribble-CHUNK @racket[CHUNK]))
    156  @(require 'scribble-doc-links)
    157 }
    158 
    159 @defform[(chunk <name> content ...)]{
    160  Same as @scribble-chunk from @racketmodname[scribble/lp2],
    161  with a few tweaks and bug fixes.}
    162 
    163 @defform[(CHUNK <name> content ...)]{
    164  Same as @scribble-CHUNK from @racketmodname[scribble/lp2],
    165  with a few tweaks and bug fixes.}
    166 
    167 @section{Memorizing and repeating chunks}
    168 
    169 @defform[(defck <name> content ...)]{
    170  Like @racket[chunk] from @racketmodname[scribble/lp2], but
    171  remembers the chunk so that it can be re-displayed later
    172  using @racket[repeat-chunk].}
    173 
    174 @defform[(repeat-chunk <name>)]{
    175  Shows again a @racket[chunk] of code previously remembered
    176  with @racket[defck]. If the @racket[<name>] starts and
    177  ends with angle brackets, they are replaced by parentheses
    178  to hint that this is not the first occurrence of this
    179  chunk, so that the name becomes @racket[|(name)|]}
    180 
    181 @section{Order of expansion of the program}
    182 
    183 The file is expanded a first time, in order to identify and
    184 aggregate the chunks of code (declared with @racket[chunk]).
    185 Then, the top-level module of the file is constructed using
    186 these chunks, and a @racket[doc] submodule is added, which
    187 contains all the surrounding text. The chunks are typeset
    188 where they appear using @racket[racketblock].
    189 
    190 The @racket[doc] submodule is declared using 
    191 @racket[module*], so that it can use 
    192 @racket[(require (submod ".."))] to use functions declared
    193 in the chunks. For example, it should be possible to
    194 dynamically compute the result of a function, and to insert
    195 it into the document, so that the value displayed always
    196 matches the implementation.
    197 
    198 When the file is expanded for the first time, however, the 
    199 @racket[(submod "..")] does not exist yet, and cannot be
    200 required. This is the case because the first expansion is
    201 performed precisely to extract the chunks and inject them in
    202 that module.
    203 
    204 To solve this problem, the following macros behave
    205 differently depending on whether the code is being expanded
    206 for the first time or not (in which case the 
    207 @racket[(submod "..")] module can be used).
    208 
    209 @defform[(if-preexpanding a b)]{
    210  Expands to @racket[a] if the code is being pre-expanded,
    211  and expands to @racket[b] if the @racket[(submod "..")]
    212  module can be used.}
    213 
    214 @defform[(when-preexpanding . body)]{
    215  Expands to @racket[(begin . body)] if the code is being
    216  pre-expanded, and expands to @racket[(begin)] otherwise.}
    217 
    218 @defform[(unless-preexpanding . body)]{
    219  Expands to @racket[(begin . body)] if the @racket[(submod "..")]
    220  module can be used, and expands to @racket[(begin)] otherwise.}
    221 
    222 @section{A note on literate programs as subsections of another document}
    223 
    224 To use @racket[include-section] on hyper-literate programs, a couple of
    225 workarounds are required to avoid issues with duplicate tags for
    226 identically-named chunks (like @racket[<*>], which is likely to always be
    227 present).
    228 
    229 @defparam[chunks-toc-prefix prefix-list (listof string?)]{
    230  We give an example for two files which are part of a hypothetical
    231  @elem[#:style 'tt "pkg"] package:
    232 
    233  @itemlist[
    234  @item{The main scribble file @filepath{main.scrbl} in the
    235    @filepath{scribblings} sub-directory includes the hyper-literate file
    236    @filepath{program.hl.rkt} located in the package's root directory, one
    237    directory level above:
    238   
    239    @codeblock[#:keep-lang-line? #t
    240  "#lang scribble/manual\n"
    241  "@title{Main document title}\n"
    242  "@include-section{../program.hl.rkt}\n"
    243  "@; could include other hyper-literat programs here\n"]}
    244  @item{To avoid issues with duplicate tag names, it is necessary to use the
    245    @racket[#:tag-prefix] option on the hyper literate program's @racket[title].
    246    Unfortunately, this breaks links to chunks in the table of contents, because
    247    scribble does not automatically add the correct prefix to them. To ensure
    248    that the links correctly work in the table of contents, it is necessary to
    249    tell hyper-literate what is the chain of document includes. The whole
    250    @filepath{program.hl.rkt} file will be:
    251   
    252    @codeblock[#:keep-lang-line? #t
    253  "#lang hyper-literate racket/base\n"
    254  "@title[#:tag-prefix '(lib \"pkg/program.hl.rkt\")]{Program title}\n"
    255  "@(chunks-toc-prefix '(\"(lib pkg/scribblings/main.scrbl)\"\n"
    256  "                      \"(lib pkg/program.hl.rkt)\"))\n"
    257  "@chunk[<*>\n"
    258  "       'program-code-here]\n"]
    259 
    260    Note that the argument for the @racket[chunks-toc-prefix] parameter is a list
    261    of string, and the strings are representations of module paths. The
    262    occurrences of @racket[lib] above are not symbols, they are just part of the
    263    string. Compare this with the following, which would be incorrect:
    264 
    265    @codeblock[#:keep-lang-line? #t
    266  "#lang hyper-literate racket/base\n"
    267  "@title[#:tag-prefix '(lib \"pkg/program.hl.rkt\")]{Program title}\n"
    268  "@; This is incorrect:\n"
    269  "@(chunks-toc-prefix '((lib \"pkg/scribblings/main.scrbl\")\n"
    270  "                      (lib \"pkg/program.hl.rkt\")))\n"
    271  "@chunk[<*>\n"
    272  "       'program-code-here]\n"]}]}
    273 
    274 @include-section[(submod (lib "hyper-literate/scribblings/diff1-example.hl.rkt")
    275                          doc)]