damn tweaks
[lambda.git] / damn.mdwn
1 Expressives such as "damn" have side effects that don't affect the
2 at-issue value of the sentence in which they occur.  What this claim
3 says is unpacked at some length here: <http://tinyurl.com/cbarker/salt/interaction/salt.pdf>.
4
5 In brief, "The man read the damn book" means the same thing as "The
6 man read the book" as far as what must be the case in the world for
7 the sentence to be true.  However, the sentence with the "damn" in it
8 in addition conveys the claim that something about the described
9 situtation is not as it should be.  (The person who is committed to
10 that claim is whoever utters the sentence.)
11
12 So we need a way of evaluating sentences that allows "damn" to launch
13 a side effect without affecting the truth conditions of the sentence
14 in which it occurs.
15
16 Furthermore, we don't want to change the meaning of "the", "man",
17 "read", or "book"---those elements are completely innocent, and
18 shouldn't be burdened with helping compute affective content.
19
20
21
22 What we did in Monday's seminar:
23
24 We start with a simulation of semantic composition:
25
26         (cons (cons 'the 'man) 
27                   (cons 'read
28                                 (cons 'the
29                                           'book)))
30
31 That evaluates to nested structure of pairs, that Scheme displays as:
32
33         ((the . man) . (read . (the . book)))
34
35 `(cons M N)` is a request to build an ordered pair out of the values M and N.
36 Scheme displays that pair as `(M . N)` You can't write the pair that way yourself:
37 if you tried to, Scheme would think you're trying to apply the function M to some arguments, which you're not, and also
38 Scheme would be confused by what argument the `.` is supposed to be. So, you say:
39
40         (cons M N)
41
42 and that evaluates to an ordered pair, and Scheme displays that ordered pair as
43
44         (M . N)
45
46 There is an underlying reason why parentheses are used both when displaying the ordered pair, and also to mean "apply this function to these arguments." However, at this point, you may well see this as a confusing overloading of parentheses to fill different syntactic roles.
47
48 Now what about the elements of our ordered pairs. Why do we say `(cons 'the 'man)`. Why are those single quotes there? Well, if you just said `(cons the man)`, Scheme would understand `the` and `man` to be variables, and it would complain that you hadn't bound these variables to any values. We don't want to build an ordered pair out of the values possessed by variables `the` and `man`. Instead, we want to just make up some dummy value THE to stand for the meaning of an object-language determiner, and some dummy value MAN to stand for the meaning of an object-language noun phrase. The notation `'the` is Scheme's way of representing a dummy, atomic value. Note there is no closing single quote, only a prefixed one. Scheme calls these dummy atomic values "symbols." That term is a bit misleading, because the symbol `'the` is not the same as the variable `the`. Neither is it the same as what's called the string `"the"`. The latter is a structured value, composed out of three character values. The symbol `'the`, on the other hand, is an atomic value. It has no parts. (The notation the programmer uses to designate this atomic value has four characters, but the value designated itself has no parts.) If you think this is all somewhat confusing, you're right. It gets easier with practice.
49
50 `'the` can also be written `(quote the)`. This is even more confusing, because here the `the` is not interpreted as a variable. (Try `(let* ((the 3)) (quote the))`.) If you come across this, just read `(quote the)` as a verbose (and perhaps misleading) way of writing `'the`, not as the application of any function to any value.
51
52 Okay, so what we've done is just create a bunch of new atomic values `'the`, `'man`, and so on. Scheme doesn't know how to do much with these. It knows for instance that `'the` is the same value as `'the` and a different value than `'man`. But it doesn't know much more than that. That's all we need or want here.
53
54 And we built a tree out of those values, representing the tree by a nested structure of pairs of leaf-labels.
55
56 The program we submitted to Scheme:
57
58         (cons (cons 'the 'man) 
59                   (cons 'read
60                                 (cons 'the
61                                           'book)))
62
63 evaluates to the nested structure of pairs that Scheme displays as:
64
65         ((the . man) . (read . (the . book)))
66
67 and that we can think of as the tree:
68 ;
69
70                                  /----------------\
71                                 /                  \
72                            /                    \
73                           /                      \
74                          /                        \                                  
75                         / \                      / \
76                    /   \                    /   \
77                   /     \                  /     \
78                  /       \                /       \
79         meaning of   meaning of   meaning of   \
80           "the"        "man"       "read"      / \
81                                                                                   /   \
82                                                                                  /     \
83                                                                                 /       \
84                                                                         meaning of  meaning of
85                                                                           "the"      "book"
86
87 Okay, let's get back to "damn."
88
89 We start by defining `damn` as a "thunk" that when applied to 0 arguments returns a trivial adjectival meaning, which we'll designate with the dummy symbol `'id`.
90
91 What's a "thunk"?
92
93 Remember, in Scheme you can have functions that take 1 value, and also functions that take 2 values, and also functions that take 0 values. The last ones are called "thunks." The thunk is not identical to the value it returns. For instance:
94
95         (lambda () 3)
96
97 is a thunk that returns the integer 3. If we bind the variable `t` to that thunk, then `t` is a function (Scheme will call it a "procedure") not an integer. Whereas `(t)` is an integer not a function.
98
99 There's no reason yet on hand for us to make `damn` be a thunk. For present purposes, we could also just define `damn` to be the symbol `'id`. But what we're going to go on to do does require us to make `damn` be a thunk. The reason for that is to postpone the evaluation of some expressions until the continuations we want to operate on are in place.
100
101 So for uniformity we're going to make `damn` be a thunk right from the beginning.
102
103 As we said, `damn` starts as a thunk that returns a trivial adjectival meaning `'id`:
104
105         (define damn (lambda () 'id))
106
107 Now we can say:
108
109         (cons (cons 'the 'man) 
110                   (cons 'read
111                                 (cons 'the
112                                           (cons (damn) 
113                                                 'book))))
114
115 and we get back:
116
117         ((the . man) . (read . (the . (id . book))))
118
119
120 Now we want to get some expressive meaning into damn. So we might try:
121
122
123         (define damn (lambda () 'bad))
124
125 But then:
126
127         (cons (cons 'the 'man) 
128                   (cons 'read
129                                 (cons 'the
130                                           (cons (damn) 
131                                                 'book))))
132
133 gives us:
134
135         ((the . man) . (read . (the . (bad . book))))
136
137 Which is not quite what we're looking for. We don't want to contribute the normal adjectival meaning of "bad" to the proposition asserted. Instead we want "bad" to be contributed as a linguistic move on the side. We might try:
138
139         (define damn (lambda () (cons 'side-effect 'bad)))
140
141 But then we'd get:
142
143
144         ((the . man) . (read . (the . ((side-effect . bad) . book))))
145
146 And the context `(the . ( ... . book))` presumably doesn't know how to interact with side-effects. That's precisely the problem we're trying to solve.
147
148
149 A promising way to handle this is with **continuations**, which you will get much more familiar with as this seminar progresses. Don't worry about not understanding what's going on quite yet. This is just an advertisement that's supposed to provoke your imagination.
150
151 Chris and others have applied the apparatus of continuations to the analysis of expressives in the papers linked above. For a simple in-class demonstration, we tried to do this.
152
153 `(call/cc (lambda k ...))` is Scheme's way of writing: bind the continuation of this very complex expression to k and evaluate the `...`.
154
155 So now we define `damn` like this:
156
157
158         (define damn (lambda () (call/cc (lambda (k) (print "bad") (k 'id)))))
159
160 Now when we do:
161
162         (cons (cons 'the 'man) 
163                   (cons 'read
164                                 (cons 'the
165                                           (cons (damn) 
166                                                 'book))))
167
168 we get something like this:
169
170         <bold>"bad"</bad> ((the . man) . (read . (the . (id . book))))
171
172 Yay! The expressive meaning has jumped out of the compositional evaluation of the main sentence, and the context `(the . (... . book))` only has to deal with the trivial adjectival meaning `'id`.
173
174 **But.** As came out in discussion, the `print` we're using here already constitutes a kind of side-effect mechanism of its own. If you say:
175
176         (define three-thunk (lambda () (print "hi") 3))
177
178 and then ask for the evaluation of:
179
180         (+ 2 (three-thunk))
181
182 you'll see something like:
183
184         <bold>"hi"</bad> 5
185
186 So the demonstration we tried in class was pedagogically flawed. It didn't properly display how continuations represent a minimally effective apparatus for representing expressive content. In fact, continuations were still doing the work, but it wasn't the explicit continuations we were writing out for you. It was instead continuations implicit in the `print` operation.
187
188 So a better demonstration would do without any device like `print` that already incorporates continuations implicitly. Any continuation-manipulation should be fully explicit.
189
190 Instead of representing the side-issue expressive contribution by printing "bad", let's instead try to build a pair of side-effect contributions and main-issue assertion. Then what we want would be something like:
191
192         ((side-effect . bad) . ((the . man) . (read . (the . (id . book)))))
193
194 Only we want to get this from the evaluation of:
195
196         (cons (cons 'the 'man) 
197                   (cons 'read
198                                 (cons 'the
199                                           (cons (damn) 
200                                                 'book))))
201
202 where `(damn)` doesn't have widest scope. And we don't want to have to recruit all the other semantic material into accepting and passing along a possible expressive argument.
203
204 How to do this?
205
206 It's not immediately clear how to do it with "undelimited" continuations, of the sort captured by `call/cc`. This is the natural first thing to try:
207
208
209         (define damn (lambda () (call/cc (lambda (k) (cons (cons 'side-effect 'bad) (k 'id))))))
210
211
212 The idea here is we capture the continuation that the thunk `(damn)` has when it gets evaluated. This continuation is bound to the variable `k`. We supply `'id` as an argument to that continuation. When the main-issues tree is all built, then we return a pair `((side-effect bad) MAIN-ISSUE-TREE)`.
213
214 However, this doesn't work. The reason is that an undelimited continuation represents the future of the evaluation of `(damn)` *until the end of the computation*. So when `'id` is supplied to `k`, we go back to building the main-issue tree until we're finished *and that's the end of the computation*. We never get to go back and evaluate the context `(cons (cons 'side-effect 'bad) ...)`.
215
216 The straightforward way to fix this is to use, not undelimited continuations, but instead a more powerful apparatus called "delimited continuations." These too will be explained in due course, don't expect to understand all this now.
217
218 A delimited continuation is captured not by using `call/cc`, but instead by using a variety of other operators. We'll use the operator `shift`. This substitutes for `call/cc`. The syntax in Scheme is slightly different. Whereas we wrote:
219
220         (call/cc (lambda k ...))
221
222 we instead write:
223
224         (shift k ...)
225
226 but the behavior is the same. It's just that now our continuation doesn't stretch until the end of the computation, but only up to some specified limit. The limit of the continuation is specified using the syntax:
227
228         (reset ...)
229
230 This is a kind of continuation-scope-marker. There are some interesting default behaviors if you don't explicitly specify where the limits are. But we'll be fully explicit here.
231
232 If a block `...` never invokes a shift, then `(reset ...)` will evaluate just the same as `...`. So for uniformity, we can designate our continuation-scopes even on computations that don't capture and manipulate continuations.
233
234 Going back to the beginning, then. We start with:
235
236         (define damn (lambda () 'id))
237
238 We evaluate:
239
240         (reset (cons (cons 'the 'man) 
241                   (cons 'read
242                                 (cons 'the
243                                           (cons (damn) 
244                                                 'book)))))
245
246 Remember, the reset isn't actually *doing* anything. It's not a function that's taking the other material as an argument. It's instead a scope-marker. Here it's not even needed (and in fact in the interactive interpreter, it wouldn't even be needed when we invoke continuations, because of the default position it takes).  But we're inserting it to be explicit and uniform.
247
248 Evaluating that gives us:
249
250         ((the . man) . (read . (the . (id . book))))
251
252
253 Now to pair that with an expressive side-issue content, we'd instead define `damn` as:
254
255         (require racket/control) ; this tells Scheme to let us use shift and reset
256         (define damn (lambda () (shift k (cons (cons 'side-effect 'bad) (k 'id)))))
257
258 And voila:
259
260         ((side-effect bad) ((the . man) . (read . (the . (id . book)))))
261
262
263 So that's the straightforward way of repairing the strategy we used in class, without using `print`. We also have to switch to using delimited continuations.
264
265
266 Ken Shan, however, pointed out a lovely way to get to the same end-point still using only undelimited continuations (`call/cc`).
267
268 (let ((pragma
269        ; An ordered pair whose first component is the assertion
270        ; operator, a unary function, and whose second component
271        ; is the meaning of "damn", a thunk.
272        (call/cc (lambda (k)
273           (cons (lambda (p) p)
274                 (lambda () (k (cons (lambda (p) (cons (cons 'side-effect 'bad) p))
275                                     (lambda () 'id)))))))))
276   (let ((assert (car pragma)) ; this binds assert to the first element of the pair pragma
277         (damn   (cdr pragma))) ; this binds damn to the second element of the pair pragma
278     (assert (cons (cons 'the 'student) (cons 'read (cons 'the (cons (damn) 'book)))))))
279
280 We won't do much to explain this. We'll just leave it for you to chew on.
281
282
283
284
285         #lang racket
286         ;(define damn (lambda () 'id))
287         (define damn (lambda () (call/cc (lambda (k) 
288                                                                           ; (k 'id)
289                                                                            (print "Something's bad")
290                                                                            (k 'id)
291                                                                            ))))
292
293         (list (list 'the (list (damn) 'man))
294                   (list 'read 
295                                 (list 'the (list (damn) 'book))))
296
297
298
299
300
301         #lang racket
302         (require racket/control)
303
304         (define damn0 (lambda ()
305                                         'id))
306
307         (define damn1 (lambda ()
308                                         (cons '("side effect" bad)
309                                                   'id)))
310
311         (define damn2 (lambda () (shift k
312                                                                         (cons '("side effect" bad) 
313                                                                                   (list (k 'id))))))
314
315         (define damn3 (lambda () (shift k
316                                                                         (list (k 'id)
317                                                                                   '("side effect" bad)))))
318
319
320 ; Now if we use damn0, our compositional semantics will work OK but
321 ; we don't yet have any expressive contribution:
322
323         (list "main content" 'i (list 'like (list 'the (damn0) 'boy)))
324         ; '("main content" i (like (the id boy)))
325
326
327 ; If we use damn1, we've added in the expressive side-effect:
328
329         (list "main content" 'i (list 'like (list 'the (damn1) 'boy)))
330         ; '("main content" i (like (the (("side effect" bad) . id) boy)))
331
332 ; However, the context (list 'the ... 'boy) is now being asked to operate
333 ; on an element (("side effect" bad) . id), and it may complain it doesn't
334 ; know what that is. It knows how to use 'id to get (list 'the 'id 'boy),
335 ; and how to use 'bad to get (list 'the 'bad 'boy), but we're supposed to
336 ; have something different here.
337
338 ; To get what we want we need to use (delimited) continuations:
339         (reset (list "main content" 'i (list 'like (list 'the (damn2) 'boy))))
340         ; '(("side effect" bad) ("main content" i (like (the id boy))))
341
342 ; or to get the side effect at the end:
343
344         (reset (list "main content" 'i (list 'like (list 'the (damn3) 'boy))))
345         ; '(("main content" i (like (the id boy))) ("side effect" bad))
346
347 ; If you're working in the interactive interpreter, the outermost "reset" here
348 ; is already in its default position, so it doesn't need to be explicitly
349 ; specified:
350
351         (list "main content" 'i (list 'like (list 'the (damn2) 'boy)))
352         ; '(("side effect" bad) ("main content" i (like (the id boy))))
353
354 ; However, if you're executing this as a file, you would need to include explicit resets.
355
356
357
358 ; Instead of using reset/shift you could use an element like "print" in
359 ; building the side-effect, as we did in class. Here you wouldn't require an
360 ; explicit continuation, but as Chris said, that's because "print" already
361 ; represents an implicit continuation.
362
363         (define damn4 (lambda () (begin (print "bad") 'id)))
364         (list "main content" 'i (list 'like (list 'the (damn4) 'boy)))
365         ; "bad"'("main content" i (like (the id boy)))
366 ;
367
368