demo2-rkt.hl.rkt (6900B)
1 #lang hyper-literate #:꩜ envlang/rkt 2 3 ꩜title[#:tag "racketfest"]{Envlang @ racketfest} 4 5 ꩜section{Use cases for macros} 6 7 ꩜subsection{Environment manipulation} 8 9 Adding bindings to the environment, getting bindings from the environment: 10 11 ꩜chunk[<use-case-bindings> 12 (let (var val) body) ;; env += {var = val} 13 (define-struct name (field ...)) ;; env += {"$name-$field" = accessor-fn} … 14 (aif condition if-true if-false) ;; env += {it = condition} 15 (match v [(cons a b) body]) ;; env += {a = (car v)} {b = (cdr v)} 16 ] 17 18 ꩜subsection{Control flow} 19 20 Changing the order of execution: 21 22 ꩜chunk[<use-case-order> 23 (if condition if-true if-false) 24 ;; can be expressed as: 25 (force (if condition 26 (λ () if-true) 27 (λ () if-false))) 28 29 (match v ([null if-null] [(cons a b) if-cons])) 30 ;; can be expressed as: 31 (force (if (null? v) 32 (λ () if-null) 33 (λ () (let ([a (car v)] [b (cdr v)]) if-cons)))) 34 35 (for/list ([x (in-list l)]) body) 36 ;; can be expressed as 37 (map (λ (x) body) l) 38 ] 39 ꩜subsection{Syntactic sugar} 40 41 ꩜chunk[<use-case-syntactic-sugar> 42 (1 + 2 * 3) ;; infix 43 (let x = 3 in (+ x 1)) 44 (for/list x in (list 1 2 3) (+ x 1)) 45 (let x:int = 3 in (+ x 1))] 46 47 ꩜subsection{Optimisations} 48 49 Optimisations are semantics-preserving compile-time transformations of the program. 50 51 ꩜chunk[<use-case-optimisations> 52 pre-calculated hash table 53 loop unrolling 54 …] 55 56 ꩜subsection{Code analysis} 57 58 Tracking and propagating annotations on the code: 59 60 ꩜chunk[<use-case-annotations> 61 typed/racket 62 source locations 63 tooltips] 64 65 ꩜section{Overview of the semantics} 66 67 ꩜chunk[<promise> 68 (f arg ...) 69 ;; is sugar for: 70 (@ f env (⧵ (env) arg) ...)] 71 72 ꩜chunk[<variables> 73 x 74 ;; is sugar for: 75 (hash-ref env x)] 76 77 ꩜section{First-class solutions} 78 79 Adding bindings to the environment, getting bindings from the environment: 80 81 ꩜subsection{Environment manipulation} 82 83 User-defined let: 84 85 ꩜chunk[<my-let> 86 (⧵ outer-env (var val body) 87 ;; evaluate body in outer env + var=val 88 (force (hash-set outer-env 89 ;; var name 90 (promise->string var) 91 ;; evaluate val in outer env 92 (force outer-env val)) 93 body))] 94 95 User-defined let with different order for the arguments: 96 97 ꩜chunk[<use-return+where> 98 (return (+ x 1) 99 where x = 123)] 100 101 ꩜chunk[<return+where> 102 (⧵ outer-env (body kw-where var kw-= val) 103 (assert (string=? (promise->string kw-where) "where")) 104 (assert (string=? (promise->string kw-=) "=")) 105 (@ my-let outer-env var val body))] 106 107 ꩜subsection{Control flow} 108 109 ꩜chunk[<my-if> 110 (⧵ outer-env (condition if-true if-false) 111 (force env ((force env condition) if-true if-false)))] 112 113 ꩜subsection{Syntactic sugar} 114 115 ꩜subsubsection{Identifiers with different meanings} 116 117 Bindings in the environment point to a table associating 118 meanings to values. See ꩜racketmodname[polysemy]. 119 120 ꩜chunk[<variables> 121 x 122 ;; becomes sugar for: 123 (hash-ref (hash-ref env x) "variable")] 124 125 ꩜racket[in] keyword used in different contexts: 126 127 ꩜chunk[<let-in-usage> 128 (let x = 3 in (+ x 1))] 129 130 ꩜chunk[<let-in> 131 (⧵ outer-env (var kw-= val kw-in body) 132 (assert (equal? (hash-ref (hash-ref env (promise->string kw-=)) 133 "let-in keyword") 134 let-in-=)) 135 (assert (equal? (hash-ref (hash-ref env (promise->string kw-in)) 136 "let-in keyword") 137 let-in-in)) 138 (@ my-let outer-env var val body))] 139 140 ꩜chunk[<for-in-usage> 141 (for/list x in (list 1 2 3) (+ x 1))] 142 143 ꩜chunk[<for-in> 144 (⧵ outer-env (var kw-in lst body) 145 (assert (equal? (hash-ref (hash-ref env (promise->string kw-in)) 146 "for keyword") 147 for-in)) 148 (@ map outer-env var val body))] 149 150 It's easy to rename just the ꩜racket["let-in keyword"] part 151 without renaming the ꩜racket["for keyword"] part. 152 153 ꩜subsubsection{Extra parentheses} 154 155 ꩜chunk[<use-let-paren> 156 (let [x 2] 157 (+ x 1))] 158 159 ꩜chunk[<let-paren> 160 (⧵ outer-env (binding body) 161 (let varval (force (hash-set "#%app" cons) binding) 162 (@ my-let outer-env (car varval) (cadr varval) body)))] 163 164 ꩜subsubsection{Infix} 165 166 ꩜chunk[<example-infix> 167 (1 + 2 * 3)] 168 169 Needs external support in the language (or overloading 170 ꩜racket[#%app]). WIP prototype using 171 ꩜link["http://www.cse.chalmers.se/~nad/publications/danielsson-norell-mixfix.pdf" "mixfix"] 172 on ꩜link["https://repl.it/@envlang/env"]{repl.it} and 173 ꩜link["https://github.com/envlang/env"]{github}. 174 175 ꩜subsubsection{Manipulating identifiers} 176 177 ꩜chunk[<example-postfix-ids> 178 (let x:int = 3 in (+ x 1))] 179 180 ꩜chunk[<postfix-ids> 181 (⧵ outer-env (var kw-= val kw-in body) 182 (let ([forced-val (force outer-env val)]) 183 (when (ends-with (promise->string var) ":int") 184 (assert int? forced-val)) 185 (@ my-let outer-env var val body)))] 186 187 ꩜section{Compile-time transformations} 188 189 Wrap parts to be evaluated at compile-time, the wrapper acts 190 like ꩜racket[unquote] where the whole program is in a 191 ꩜racket[quasiquote]. 192 193 ꩜chunk[<compile-time-proposal> 194 (run-time 195 (let ([x (compile-time (+ 1 2 3))]) 196 (* x x)))] 197 198 ꩜chunk[<compile-time-proposal-equivalent> 199 `(let ([x ,(+ 1 2 3)]) 200 (* x x))] 201 202 Semantics-preserving: removing the ꩜racket[run-time] and 203 ꩜racket[compile-time] markers must give an equivalent 204 program. 205 206 ꩜section{Code analysis} 207 208 ꩜subsection{Type checking} 209 210 These environment manipulations can be modeled with row types: 211 212 ꩜chunk[<row-type-example> 213 (λ (x : (struct [foo : int] [bar : string] . ρ)) 214 : (struct [foo : int] [quux : int] . ρ) 215 (x without .bar 216 with .quux = (+ x.foo (string->int x.bar))))] 217 218 219 ꩜subsection{Implemented within the language} 220 221 … to be explored? 222 223 ꩜section{Example use} 224 225 ꩜chunk[<program> 226 (my-let x 3 227 (let-paren [x 3] 228 (let-postfix x:int = 3 in 229 (return (for/list z in (compile-time (list 1 2 3)) 230 (+ z y)) 231 where y = (+ 1 x)))))] 232 233 234 ꩜chunk[<env+program> 235 (let* ([my-let <my-let>] 236 [return <return+where>] 237 [my-if <my-if>] 238 [let-paren <let-paren>] 239 [let-postfix <postfix-ids>] 240 ) 241 <program>)] 242 243 ꩜chunk[<*> 244 ;;<env+program> 245 ]