remember.scrbl (11150B)
1 #lang scribble/manual 2 @require[@for-label[remember 3 racket/base]] 4 5 @title{Remember: storage for macros which is persistant across compilations} 6 @author[@author+email["Suzanne Soy" "racket@suzanne.soy"]] 7 8 @defmodule[remember] 9 10 This library is implemented using literate programming. The 11 implementation details are presented in 12 @other-doc['(lib "remember/remember-implementation.hl.rkt")]. 13 14 This module allows macros to remember some values across 15 compilations. Values are grouped by @racket[_category], so 16 that multiple macros can use this facility without 17 interfering with each other. The @racket[_category] is 18 simply a symbol given when remembering the value. 19 20 The list of all remembered values for a given 21 @racket[_category] is returned by @racket[get-remembered], 22 and it is possible to check if a single value has been 23 remembered using @racket[remembered?]. 24 25 Values are loaded from files using 26 @racket[remember-input-file] and @racket[remember-io-file]. 27 An output file can be set with 28 @racket[remember-output-file] and 29 @racket[remember-io-file]. 30 31 When an output file has been declared, new values passed to 32 @racket[remember-write!] are marked as 33 @racket[remembered-or-written?] and appended to that file 34 (more precisely, the expression 35 @racket[(remembered! _category _value)] is appended to the 36 file, followed by a newline). 37 38 When initially created by the user, the output file should 39 contain the code below, which will be followed by the 40 automatically-generated 41 @racket[(remembered! _category _value)] statements: 42 43 @codeblock[#:keep-lang-line? #t]|{ 44 #lang racket 45 (require remember)}| 46 47 The @racket[remembered!] macro indicates an 48 already-remembered value, and is typically used inside input 49 files. The @racket[for-syntax] function 50 @racket[remembered-add!] can also be used instead, to mark a 51 value as @racket[remembered?] without adding it to any file 52 (this can be useful for values which should implicitly be 53 remembered). 54 55 @defproc[#:kind "for-syntax procedure" 56 (get-remembered [category symbol?]) list?]{ 57 Returns a list of all values that have been remembered for 58 the given @racket[category] (i.e. all values passed as the 59 second argument to @racket[remembered-add!], 60 @racket[remember-write!] or @racket[remembered!], with the given 61 category as the first argument).} 62 63 @defproc[#:kind "for-syntax procedure" 64 (remembered-add! [category symbol?] [value any/c]) void?]{ 65 Marks the given @racket[value] as remembered in the given 66 @racket[category]. If the same value is remembered twice 67 for the same category, the second occurrence is ignored 68 (i.e. values are stored in a distinct @racket[set] for each 69 category). 70 71 This @racket[for-syntax] procedure is called by the 72 @racket[remembered!] macro, but can also be executed on its 73 own.} 74 75 @defproc[#:kind "for-syntax procedure" 76 (remembered? [category symbol?] [value any/c]) boolean?]{ 77 Checks whether the given @racket[value] has already been 78 added to the set of remembered values for the given 79 @racket[category].} 80 81 @defproc[#:kind "for-syntax procedure" 82 (remembered-or-written? [category symbol?] [value any/c]) boolean?]{ 83 Checks whether the given @racket[value] has already been 84 added to the set of remembered values for the given 85 @racket[category], or if it was freshly written to a file 86 during the current expansion.} 87 88 @defproc[#:kind "for-syntax procedure" 89 (remember-write! [category symbol?] [value any/c]) void?]{ 90 Adds the given @racket[value] to the current 91 @racket[remember-output-file] for the given category. More 92 precisely, the expression 93 @racket[(remembered! category value)] is appended to the 94 file, followed by a newline. 95 96 If the value is already @racket[remembered-or-written?], 97 then the file is left unchanged, i.e. two or more calls to 98 @racket[remember-write!] with the same @racket[category] 99 and @racket[value] will only append an expression to the 100 file the first time. 101 102 The value is also added to the set of 103 @racket[remembered-or-written?] values, so that subsequent 104 calls to @racket[remembered-or-written?] return 105 @racket[#t] for that category and value. Calls to 106 @racket[remembered?] will be unaffected, and will still 107 return @racket[#f]. If some declarations are created by a 108 library based on the @racket[get-remembered] set, it is 109 therefore possible to check whether a value was already 110 present, or if it was added by a subsequent 111 @racket[remember-write!].} 112 113 @defproc[#:kind "for-syntax procedure" 114 (remembered-error! [category symbol] [stx-value syntax?]) void?]{ 115 Produces a delayed error indicating that this value has 116 not been remembered, but was added to the output file. 117 118 This procedure just triggers the error, and is not 119 concerned with actually adding the value to the output 120 file. 121 122 The error is added in a lifted declaration which is 123 inserted at the end of the current module, using 124 @racket[syntax-local-lift-module-end-declaration]. It 125 should therefore be triggered only when the compilation 126 reaches the end of the file, if no other error was raised 127 before. 128 129 This allows as many @racket[remembered-error!] errors as 130 possible to be accumulated; all of these are then shown 131 when the file is fully expanded. The goal is to be able to 132 add all values to the output file in a single run, instead 133 of aborting after each value which is not remembered. This 134 would otherwise require recompiling the program once for 135 each value which is not initially remembered. 136 137 TODO: it would be nice to factor out the delayed error 138 mechanism into a separate package, so that multiple 139 libraries can add errors, and all of them get reported, 140 without one preventing the others from executing. This 141 function would likely keep the same signature, and just 142 delegate to the delayed-error library.} 143 144 @defparam[disable-remember-immediate-error disable? boolean? #:value #f]{ 145 The @racket[disable-remember-immediate-error] parameter allows code to 146 temporarily prevent @racket[remembered-error!] from lifting a delayed error. 147 This can be useful for example when calling @racket[remembered-error!] from a 148 context where @racket[(syntax-local-lift-context)] is @racket[#false], e.g. 149 outside of the expansion of a macro, but within a @racket[begin-for-syntax] 150 block. 151 152 The error is still put aside, so that if a delayed error was triggered by 153 another call to @racket[remembered-error!], the error will still be included 154 with the other delayed errors. If no delayed error is triggered during 155 macro-expansion, the error that was put aside will be ignored. To prevent 156 this from happening, call @racket[lift-maybe-delayed-errors] within a context 157 where lifts are possible.} 158 159 @defproc[(lift-maybe-delayed-errors) void?]{ 160 Uses @racket[syntax-local-lift-module-end-declaration] or 161 @racket[syntax-local-lift-expression], depending on the context, to lift an 162 expression which will trigger delayed errors, if any. If no delayed errors 163 have been recorded by @racket[remembered-error!] when the lifted form is 164 executed, then nothing will happen and expansion will proceed. 165 166 Note that when @racket[(syntax-transforming-module-expression?)] returns 167 @racket[#false], @racket[syntax-local-lift-expression] is used. The lifted 168 form is then run as part of the current expansion pass, before the contents of 169 any @racket[let] forms are expanded. This means that calls to 170 @racket[remembered-error!] must not happen within the expansion of nested 171 @racket[let] forms (with respect to the @racket[let] form being expanded (if 172 any) when @racket[lift-maybe-delayed-errors] is called), as they would add 173 delayed errors too late, i.e. after the lifted form got executed.} 174 175 @defform[(remember-input-file name) 176 #:grammar ([name string?])]{ 177 The file is loaded with @racket[require], but no 178 identifier is imported from that module. Instead, 179 @racket[remembered?] relies on its internal mutable 180 @racket[for-syntax] hash table which stores remembered 181 values associated to their category. 182 183 @racket[remembered-values]. Values are added to the hash 184 via the @racket[remembered!] macro. The @racket[name] file 185 should therefore @racket[require] the 186 @racketmodname[remember] library, and contain a number of 187 calls to @racket[remembered!], each adding a new value to 188 the mutable hash.} 189 190 @deftogether[ 191 (@defform*[((remember-output-file) 192 (remember-output-file name)) 193 #:grammar ([name (or/c string? false?)])] 194 @defproc*[#:kind "for-syntax parameter" 195 #:link-target? #f 196 ([(remember-output-file) (or/c string? false?)] 197 [(remember-output-file [name (or/c string? false?)]) void?])] 198 )]{ 199 Indicates that new values added via 200 @racket[remember-write!] should be appended to the file 201 @racket[name]. More precisely, the expression 202 @racket[(remembered! _category _value)] is appended to the 203 file, followed by a newline. 204 205 Note that if the @racket[_value] given to 206 @racket[remember-write!] is already registered in an input 207 file with @racket[remembered!] for the same category, it 208 will not be appended to the output file. 209 210 For now there can only be one @racket[output] file at the 211 same time, any call to @racket[remember-output-file] 212 overrides the setting from previous calls. Future versions 213 of this library may offer the possibility to specify an 214 output file per @racket[_category]. 215 216 The special value @racket[#f] indicates that there is no 217 output file, in which case @racket[remember-write!] simply 218 marks the @racket[value] as 219 @racket[remembered-or-written?] for that category, without 220 altering any file. 221 222 This identifier exists both as a macro and a for-syntax 223 parameter. When called without any argument, it expands to 224 (for the macro) or returns (for the for-syntax parameter) 225 the last value set using either the macro or by passing an 226 argument to the for-syntax parameter.} 227 228 @defparam[remember-output-file-parameter output-file 229 (or/c path-string? false?) 230 #:value #f]{ 231 This for-syntax parameter that new values added via @racket[remember-write!] 232 should be appended to the file whose name is stored within the parameter. 233 234 The @racket[remember-output-file] macro simply sets this parameter.} 235 236 @defform[(remember-io-file name) 237 #:grammar ([name string?])]{ 238 Indicates that calls to @racket[remembered!] in this file 239 should be taken into account, and that new values added 240 with @racket[remember-write!] should be appended to this 241 file. 242 243 It is equivalent to: 244 @racketblock[(remember-input-file name) 245 (remember-output-file name)]} 246 247 @defform[(remembered! category value) 248 #:grammar ([category identifier?])]{ 249 Marks the given @racket[value] as remembered in the given 250 @racket[category]. If the same value is remembered twice 251 for the same category, the second occurrence is ignored 252 (i.e. values are stored in a distinct @racket[set] for each 253 category). 254 255 Calls to this macro are usually present in an input file 256 loaded with @racket[remember-input-file] or 257 @racket[remember-io-file], but can also be inserted in the 258 main file or any other file loaded with @racket[require].}