anaphoric.scrbl (7130B)
1 #lang scribble/manual 2 @require[@for-label[anaphoric 3 racket/base]] 4 5 @title{Anaphoric macros} 6 @author[@author+email["Suzanne Soy" "racket@suzanne.soy"]] 7 8 @defmodule[anaphoric] 9 10 @section{Overview} 11 12 This package provides anaphoric versions of @racket[if], 13 @racket[when], @racket[cond], @racket[map], and @racket[filter]. These bind the syntax 14 parameter @racket[it] to the value produced by the 15 condition expression. 16 17 @racketblock[(aif (member 'a lst) 18 (displayln it) 19 (displayln "not found"))] 20 21 @racketblock[(awhen (member 'a lst) 22 (displayln it))] 23 24 @racketblock[(acond 25 [(member 'a lst) (displayln it)] 26 [(member 'b lst) (displayln it)] 27 [else (displayln "not found")])] 28 29 In the @racket[else] clause of @racket[acond] and in the 30 else branch of @racket[aif], the @racket[it] syntax 31 parameter keeps its value. This means it keeps the value 32 bound by the surrounding conditional, if any. Otherwise it 33 acts exactly as when it is used at the top-level, and raises 34 a syntax error. 35 36 @racketblock[(aif 'first 37 (aif (eq? 'second 'no) 38 'not-executed 39 (displayln it)) 40 'not-executed)] 41 42 In the example above, @racket[(displayln it)] prints 43 @racket['first]. In the example below, 44 @racket[(displayln it)] raises a syntax error, as it appears 45 in a sequence of else branches: 46 47 @racketblock[(aif (eq? 'first 'no) 48 'not-executed 49 (aif (eq? 'second 'no) 50 'not-executed 51 (displayln it)))] 52 53 This package also provides the hygienic versions 54 @racket[if-let], @racket[when-let] and @racket[cond-let], 55 for which the user needs to specify an identifier instead of 56 using @racket[it]. 57 58 @section{The anaphoric conditionals @racket[aif], 59 @racket[awhen] and @racket[acond]} 60 61 @defidform[it]{ 62 Syntax parameter which acts as a rename transformer for 63 the result of the condition expression, when bound by 64 @racket[aif], @racket[awhen] or @racket[acond]. 65 66 Raises a syntax error when used outside of the 67 @racket[_true-branch] of an @racket[aif] or the body of an 68 @racket[awhen] or the body of a non-@racket[else] case in 69 @racket[acond].} 70 71 @defform[(aif condition true-branch false-branch)]{ 72 Variant of @racket[if] which binds @racket[it] to the 73 value of @racket[condition] in @racket[true-branch]. 74 @racket[condition] is only evaluated once. In the 75 @racket[false-branch], @racket[it] is left unchanged.} 76 77 @defform[(awhen condition body ...+)]{ 78 Variant of @racket[when] which binds @racket[it] to the 79 value of @racket[condition] in @racket[body ...+]. 80 @racket[condition] is only evaluated once.} 81 82 @defform*[#:literals (else) 83 [(acond [conditionᵢ bodyᵢ ...+] ...) 84 (acond [conditionᵢ bodyᵢ ...+] ... [else body ...+])]]{ 85 Variant of @racket[cond] which binds @racket[it] to the 86 corresponding @racket[conditionᵢ] in the non-@racket[else] 87 cases. More precisely, in each @racket[bodyᵢ ...+], 88 @racket[it] is bound to the value of the corresponding 89 @racket[conditionᵢ]. Each @racket[conditionᵢ] is evaluated 90 at most once (evaluation stops at the first successful 91 @racket[conditionᵢ]).} 92 93 @defform*[[(aand) 94 (aand conditionᵢ ... body)]]{ 95 Variant of @racket[and] which binds @racket[it] 96 to the value of each @racket[conditionᵢ], in scope within the 97 next @racket[conditionᵢ] or @racket[body]. More precisely, the value 98 of each @racket[conditionᵢ] can be referred to as @racket[it] in 99 the following @racketvarfont{conditionᵢ₊₁}, and the value of the last 100 @racket[conditionᵢ] can be referred to as @racket[it] in the 101 @racket[body]. If there are no @racket[conditionᵢ], i.e. when 102 writing, @racket[(aand body)], then @racket[it] retains its original 103 binding (which means that @racket[it] could be unbound, e.g. if no 104 other @racketmodname[anaphoric] form wraps this one). 105 106 Each @racket[condition] is evaluated at most once, and 107 evaluation stops at the first false condition. The 108 @racket[body] is only evaluated when every 109 @racket[conditionᵢ] is successful. 110 } 111 112 @section{The hygienic versions @racket[if-let], 113 @racket[when-let] and @racket[cond-let]} 114 115 @defform[(if-let [identifier condition] true-branch false-branch)]{ 116 Variant of @racket[if] which binds @racket[identifier] to 117 the value of @racket[condition] in @racket[true-branch]. 118 @racket[condition] is only evaluated once. In the 119 @racket[false-branch], @racket[identifier] is left unchanged.} 120 121 @defform[(when-let [identifier condition] body ...+)]{ 122 Variant of @racket[when] which binds @racket[identifier] to 123 the value of @racket[condition] in @racket[body ...+]. 124 @racket[condition] is only evaluated once.} 125 126 @defform*[#:literals (else) 127 [(cond-let [[identifierᵢ conditionᵢ] bodyᵢ ...+] ...) 128 (cond-let [[identifierᵢ conditionᵢ] bodyᵢ ...+] ... [else body ...+]) 129 (cond-let identifier [conditionᵢ bodyᵢ ...+] ...) 130 (cond-let identifier [conditionᵢ bodyᵢ ...+] ... [else body ...+])]]{ 131 Variant of @racket[cond] which binds each 132 @racket[identifierᵢ] to the corresponding 133 @racket[conditionᵢ] in the non-@racket[else] cases. More 134 precisely, in each @racket[bodyᵢ ...+], the corresponding 135 @racket[identifierᵢ] is bound to the value of the 136 corresponding @racket[conditionᵢ]. 137 138 The last two variants are shorthands for using the same 139 @racket[identifier] in all cases (except the @racket[else] 140 case). 141 142 Each @racket[conditionᵢ] is evaluated at most once 143 (evaluation stops at the first successful 144 @racket[conditionᵢ]).} 145 146 @defform*[[(and-let) 147 (and-let [identifier conditionᵢ] ... body)]]{ 148 Variant of @racket[and] which binds each @racket[identifier] 149 to the value of its @racket[conditionᵢ], in scope within every 150 @racket[conditionᵢ] afterwards as well as in @racket[body]. 151 152 Each @racket[conditionᵢ] is evaluated at most once, and 153 evaluation stops at the first false condition. The 154 @racket[body] is only evaluated when every 155 @racket[conditionᵢ] is successful. 156 } 157 158 @section{Anaphoric map and filter} 159 160 @defform[(amap body lst)]{ 161 Anaphoric @racket[map]. Binds the syntax parameter @racket[it] 162 in the @racketid[body], and maps it over the list @racketid[lst]. Effectively the same 163 as wrapping the @racketid[body] in a @racket[lambda] with an @racket[it] parameter. Unlike @racket[map], @racket[amap] 164 only works on a single list. 165 166 @racket[amap] works with nested function calls: 167 168 @racketblock[(amap (string-append (string-upcase it) "!") 169 '("apple" "banana"))] 170 171 The syntax parameter @racket[it] may be used multiple times in the procedure: 172 173 @racketblock[(amap (* it it) '(1 2 3))] 174 } 175 176 @defform[(afilter body lst)]{ 177 Anaphoric @racket[filter]. Binds the syntax parameter @racket[it] 178 in the @racketid[body], and filters the list @racketid[lst] using it. Effectively the same 179 as wrapping the body in a @racket[lambda] with an @racket[it] parameter. 180 181 @racket[afilter] works with nested function calls: 182 183 @racketblock[(afilter ((* it it) . > . 50) lst)] 184 }