import imp
imp.reload(lamb.parsing)
reload_lamb()
Based on Chris Barker, "Continuations and the Nature of Quantification", Natural Language Semantics, 2002.
This notebook implements a version of Barker's account of scope using continuations. Barker develops a semantic account of quantifier scope that does not rely at all on LF manipulations etc., but rather complicates the types of lexical items and composition rules in a systematic way to make them more general. This notebook develops the analysis in roughly the sequence found in Barker's paper; first I show how to continuize ordinary meanings, and how to write a quantifier in this framework. I then turn to scope ambiguities, scope freezing, and finally inverse linking / QDP-within-QDP cases.
Initial note: because I do not use rule-to-rule translation, and instead use general composition rules, the predictions of the system in terms of possible composition paths are somewhat closer to those developed in Yusuke Kubota and Wataru Uegaki, "Continuation-based semantics for Conventional Implicatures: The case of Japanese benefactives", Proceedings of SALT 19, 2009. Basically, a system with general rules tends to overgenerate possible composition paths that converge on the same meaning; as far as I am aware the resultant S-level meanings (filtered to be the appropriate type) are the same as Barker's system. I will note several places where this is relevant.
Continuations are notoriously hard to explain, and I will do so here mainly by example. I recommend using the notebook to inspect objects that are mysterious, check on the details of derivations, etc.
An ordinary meaning can be continuized by turning it into a function from its continuation to itself applied as an argument to the continuation. The continuation represents something like the "next state" of the computation. The continuation of X is always a function from X to type t; the output type is because all derivations (by assumption) end in ordinary type t.
The effect is perhaps easiest to see when continuizing type t, exemplified by raining1
(the proposition that it is raining) and raining2
(its continuized form) below. The continuation of something of ordinary type t
is a function that maps that type to the output type, namely t
. Crucially, if the identity function for <t,t>
is combined via function application with a continuized denotation of this type, we always get back its ordinary denotation. This is always true of the sentence-level meanings, which I assume to be of type <<t,t>,t>
following Barker. The identity function can be thought of as an empty context of sorts.
Two more examples for type e, and type <e,t>
, are given as well.
%%lamb
||raining1|| = Raining_t # reminder: hit shift-enter to run
||raining2|| = L f_<t,t> : f(Raining_t)
||john1|| = John_e
||john2|| = L f_<e,t> : f(John_e)
||cat1|| = L x_e : Cat(x)
||cat2|| = L f_<<e,t>,t> : f(L x_e : Cat(x))
INFO (meta): Coerced guessed type for 'Cat_t' into <e,t>, to match argument 'x_e' INFO (meta): Coerced guessed type for 'Cat_t' into <e,t>, to match argument 'x_e'
Mapping any ordinary denotation to its continuation is relatively straightforward at a mechanical level, at least. econt
below illustrates a combinator that does this for type e.
%%lamb
econt = L x_e : L f_<e,t> : f(x)
econt(john1.content).reduce_all() # we get back the content of john2
The following function generalizes this; given any type it constructs the continuization combinator for that type. This is illustrated using cat1
.
%%lamb
continuize = L x_X : L f_<X,t> : f(x)
This function could be used for e.g. constructing a typeshift or unary composition operation (as in the Kubota and Uegaki system). Here I will use it slightly differently, to construct a 'lexical transform' that can be applied in metalanguage definitions. This is indicated by the =<> notation below.
def continuize_lex(te):
new_te = continuize(te).reduce_all()
return new_te
#continuize_lex(cat1.content)
lamb.parsing.eq_transforms["cont"] = continuize_lex
%%lamb
||cat2|| =<cont> L x_e : Cat(x)
||dance2|| =<cont> L x_e : Dance(x)
||john2|| =<cont> John_e
||the|| =<cont> L f_<e,t> : Iota x_e : f(x)
INFO (meta): Coerced guessed type for 'Cat_t' into <e,t>, to match argument 'x_e' INFO (meta): Coerced guessed type for 'Dance_t' into <e,t>, to match argument 'x_e'
While in some cases standard FA can actually work and even produce effectively the right result, this isn't really what we want to do. Note for example that given the above types, and regular function application, there would be no way to compose the
with cat
.
A continuized apply is somewhat complicated and easiest to see through working examples. It needs to, effectively, sequence continuations. The following example is a combinator that continuizes just the composition of a property (ordinary type <e,t>
) with its argument. b
is roughly the decontinuized version of the function, and c
the decontinuized version of the argument; abar
is the continuation argument for the whole expression. Below this I illustrate this with dance
and john
, with some of the internal steps of the derivation revealed.
%%lamb
cfaet = L f_<<<e,t>,t>,t> : L arg_<<e,t>,t> : L abar_<t,t> : f(L b_<e,t> : arg(L c_e : abar(b(c))))
(cfaet(dance2.content)(john2.content)).reduce_all().derivation
To build a compositional rule along these lines we can generate a combinator like this at arbitrary values of function type and argument type. The following combinator looks quite complicated, but the heart of it is b(c)
. b
is the decontinuized version of the function f
, and c
is the decontinuized version of the argument arg
.
Note: Barker implements application as a set of rule-to-rule (i.e. category specific) operations. I'm not personally a fan of this style (and in any case the lambda notebook doesn't currently have the infrastructure) so I will implement things via general composition rules. This is important to keep in mind, since the implementation here overgenerates quite a bit in consequence. This is very similar to the Kubota and Uegaki system mentioned in the intro.
%%lamb
contapply = L f_Z : L arg_Z1 : L abar_Z2 : f(L b_<X,X1> : arg(L c_X : abar(b_<X,X1>(c_X))))
Here are a few examples of generated combinators in action.
contapply(cat2.content)(john2.content)
contapply(cat2.content)(john2.content).reduce_all()
contapply(continuize(cat1.content))(continuize(john1.content)).reduce_all()
contapply(the.content)(cat2.content)
contapply(the.content)(cat2.content).reduce_all()
Add this operation as a composition operation named CA
. Below this are a few examples of the rule in action.
system = lang.td_system.copy()
system.remove_rule("FA")
system.remove_rule("PA")
system.remove_rule("PM")
#system.add_rule(ca_op)
system.add_binary_rule(contapply, "CA")
lang.set_system(system)
system
(john2 * dance2).tree()
%%lamb
||saw|| =<cont> L y_e : L x_e : Saw(x,y)
||mary|| =<cont> Mary_e
INFO (meta): Coerced guessed type for 'Saw_t' into <(e,e),t>, to match argument '(x_e, y_e)'
john2 * (saw * mary)
(john2 * (saw * mary)).tree()
At this point it is time to turn to quantifiers. Items like everyone
have continuized types, but are not generated by continuizing an ordinary meaning. Rather, they are written as continuized types that manipulate their continuations. In fact, their standard GQ entry is their continuized entry. For comparison, a continuized version of ordinary "everyone" is given as everyone0
. (All of these ignore animacy.) While these two entries for everyone
get the same result in subject position (shown below), they do it in different ways.
%%lamb
||someone|| = L xbar_<e,t> : Exists x_e : xbar(x)
||everyone|| = L xbar_<e,t> : Forall x_e : xbar(x)
||everyone0|| =<cont> L f_<e,t> : Forall x_e : f(x)
everyone * (saw * mary)
everyone0 * (saw * mary)
everyone0
will not work in object position (as in standard approaches cf. Heim and Kratzer), but the Barker's versions will, effectively for "free" (once the infrastructure is accepted). The first example shows what happens with the continuized ordinary generalized quantifer; the resulting errors are generated inside the two possible continuized apply combinators. The other examples demonstrate Barker's quantifiers in object position.
(saw * everyone0)
mary * (saw * everyone)
everyone * (saw * someone)
Multiple quantifiers work as well, shown above. This generates inverse scope; we will attend to surface scope shortly. I started with inverse scope because Barker does, but I'm not aware of any real significance to this choice.
To get surface scope, we need a second version of application which prioritizes continuations differently.
%%lamb
contapply2 = L f_Z : L arg_Z1 : L abar_Z2 : arg(L c_X : f(L b_<X,X1>: abar(b_<X,X1>(c_X))))
system = lang.td_system.copy()
system.remove_rule("FA")
system.remove_rule("PA")
system.remove_rule("PM")
system.add_binary_rule(contapply, "CA")
system.add_binary_rule(contapply2, "CA2")
lang.set_system(system)
system
And on to the interesting stuff. In general each of these rules can apply to any continuized type, so now we do overgenerate the same result; the lambda notebook will collapse these because they are equivalent. (Barker overgenerates less, because of his use of rule-to-rule translation.) All the results are right, so this isn't really a problem. When there are multiple quantifiers, we generate both readings.
everyone * (saw * mary)
everyone * (saw * someone)
(someone * (saw * everyone))
If you're wondering about the overgeneration, this can be inspected in a number of ways, but the easiest here is with a tree. The tree will show alternative composition operations that lead to the same result. For the inverse scope reading, either CA or CA2 can apply to compose the verb with the object:
(someone * (saw * everyone))[1].tree()
What about quantificational determiners? Barker presents two treatments. In the text, regular FA is allowed to combine a determiner with its complement (in a rule-to-rule fashion). In the appendix, a different general treatment not requiring FA, but using choice functions, is presented. For the moment I stick to the text version, and allow FA as a general possibility (so I will overgenerate more than Barker did, a la Kubota and Uegaki). Later we'll need the choice function version.
%%lamb
||every|| = L pbar_<<<e,t>,t>,t> : L xbar_<e,t> : pbar(L f_<e,t> : Forall x_e : (f(x) >> xbar(x)))
system = lang.td_system.copy()
#system.remove_rule("FA")
system.remove_rule("PA")
system.remove_rule("PM")
system.add_binary_rule(contapply, "CA")
system.add_binary_rule(contapply2, "CA2")
lang.set_system(system)
def tfilter_fun(i):
return (i.type == lang.tp("<<t,t>,t>"))
tfilter = lang.CRFilter("S-filter", tfilter_fun)
The latter part of the above box prunes down on a bit of overgeneration; any derivation that does not result in a sentence type (e.g. <<t,t>,t>
in a continuized setting) is eliminated.
Another form of overgeneration is that we can see that the CA rule can also apply in D-N combination, though the derivation resulting from this choice won't converge so it doesn't especially matter.
(every * cat2).tree()
r = (every * cat2) * (saw * someone)
tfilter(r)
r[1].tree()
In order to make tensed S nodes a scope island, Barker provides a different composition rule for S$\rightarrow$NP VP nodes that blocks continuation passing. In a setting with generalized composition rules, this needs to be done a bit differently. One can define an operator that performs the same function as this rule, and this operator might be lexically instantiated by e.g. that
. I've named this operator Disrupt
:
%%lamb
||Disrupt|| = L s_<<t,t>,t> : L abar_<t,t> : abar(s(L p_t : p))
It is actually somewhat hard to find examples that really use this in an extensional setting with no binding/traces; Barker provides conjunction. I'm not (currently) treating conjunction in this fragment so we must search for something a little different. One relevant case is the simplest analysis of embedding predicates like it is false that
as negation. Empirically, quantifiers cannot scope over this, though they can scope over ordinary negation.
%%lamb
||iift|| =<cont> L p_t : ~p
We can first see that Disrupt has a non-substantive but visible effect on simple quantificational sentences. In one case, the quantifier scopes under the continuation, in the other case, over.
tfilter(Disrupt * (someone * dance2))
tfilter(someone * dance2)
This now does something interesting when iift
(it is false that) composes with or without Disrupt:
tfilter(iift * (someone * dance2))
r2 = tfilter(iift * (Disrupt * (someone * dance2)))
r2
Negation in these derivations is scopally inert, and quantifiers scope over it. In fact this reveals an interesting property of this system: whatever items are scopally potent must scope over whatever material is scopally inert, up to operators like Disrupt or other scopal elements.
If you think about scope as a form of 'projective meaning', then this means that in a framework like the present with only at-issue meaning as the other choice, scopal elements project maximally. This fact was exploited by Kubota and Uegaki in using continuations for Potts-style Conventional Implicature (CI) content, which should project maximally. It is worth noting, however, that as far as I can see scope is not CI content as such, and we would certainly not want scopal ambiguities to interact with projective meanings in the way that combining the two uses of continuations would involve. (At the least, tensed finite clauses are not generally a scope island for CI content.) In summary, it is currently unclear (to me) that continuations can be used as a general mechanism for projective meaning displacement in a compositional semantics, because there are different empirical properties for different types of projective meaning; I don't yet see how it could account for both quantifier scope and CI projection at the same time.
Moving on, one can generate a non-scopally-inert negation by using a similar trick to what was performed on the quantifiers, i.e. applying the content of the item after the continuation. This is shown below:
%%lamb
||sneg|| = L f_<<t,t>,t> : ~ f(L p_t : p)
Now negation of this form must take widest form w.r.t. scopally inert elements, but will scope ambiguously w.r.t. scopal elements.
tfilter(sneg * (someone * dance2))
The LF analysis of scope has in general had a hard time with the scope of DPs within DPs. First, in many cases the judgments aren't very clear. But to the extent that they are clear, one must block some of the readings. Barker's proposal is that the possible readings just fall out of the architecture of continuation-based scope. Let's see how this works.
%%lamb
||no|| = L pbar_<<<e,t>,t>,t> : L xbar_<e,t> : pbar(L f_<e,t> : (~ (Exists x_e : (f(x) & xbar(x)))))
||a|| = L pbar_<<<e,t>,t>,t> : L xbar_<e,t> : pbar(L f_<e,t> : (Exists x_e : (f(x) & xbar(x))))
||fromP|| =<cont> L x_e : L f_<e,t> : L y_e : f(y) & From(y,x)
||france|| =<cont> France_e
||fcountry|| =<cont> L x_e : ForeignCountry(x)
INFO (meta): Coerced guessed type for 'From_t' into <(e,e),t>, to match argument '(y_e, x_e)' INFO (meta): Coerced guessed type for 'ForeignCountry_t' into <e,t>, to match argument 'x_e'
Just as a sanity check, something like "no cat from france danced" should compose as is, and generate one scoping. This works!
tfilter((no * (cat2 * (fromP * france))) * dance2)
Now the real test: what happens with
r = tfilter((no * (cat2 * (fromP * (a * fcountry)))) * dance2)
r
Failure! Only one scoping is generated. What's going on?
It turns out that the hack/presentational strategy used in the body of Barker's paper to combine NPs with Ds via regular function application doesn't allow for inverse scoping. This is unsurprising when you think about it as this operation's effect on scope is a bit like what happens with Disrupt
: continuations are effectively trapped.
We'll have to move to the version in Barker's appendix. Before doing that, you may want to inspect what composition paths are being found, by looking at r
.
r[0].tree()
In the appendix, Barker treats quantificational determiners as quantifiers over choice functions of type <<e,t>,e>
, that is, functions that map properties into individuals. It turns out, somewhat magically at first glance, that in consequence quantificational determiners are the right type to compose with property-denoting sisters via CA
or CA2
and generate a GQ type. This requires staring at the derivation for a while but the basic idea is that the continuation of a GQ should be type <e,t>
, and so its input needs to be type e
, and a choice function for property types bridges this need with the continuized-property-denoting sister.
Strictly speaking, one should do this for everyone
and someone
as well, but it is pedagogically simpler not to, so I'll leave them as is.
%%lamb
||a|| = L dbar_<<<e,t>,e>,t> : Exists f_<<e,t>,e> : dbar(f)
||no|| = L dbar_<<<e,t>,e>,t> : ~(Exists f_<<e,t>,e> : dbar(f))
||every|| = L dbar_<<<e,t>,e>,t> : (Forall f_<<e,t>,e> : dbar(f))
every * cat2
(every * cat2)[0].tree(derivations=True)
$[\![\mathbf{\text{cat2}}]\!]^{}_{\left\langle{}\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{},t\right\rangle{}}$ | |
$=$ | ${[\lambda{} x_{\left\langle{}e,t\right\rangle{}} \: . \: \lambda{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{}} \: . \: {f}({x})]}(\lambda{} x_{e} \: . \: {Cat}({x}))$ |
$=$ | $\lambda{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{}} \: . \: {f}(\lambda{} x_{e} \: . \: {Cat}({x}))$ |
$[\![\mathbf{\text{[every cat2]}}]\!]^{}_{\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{}}$ | |
$=$ | ${[\lambda{} f_{\left\langle{}\left\langle{}\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{},t\right\rangle{},t\right\rangle{}} \: . \: \lambda{} arg_{\left\langle{}\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{},t\right\rangle{}} \: . \: \lambda{} abar_{\left\langle{}e,t\right\rangle{}} \: . \: {f}(\lambda{} b_{\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{}} \: . \: {arg}(\lambda{} c_{\left\langle{}e,t\right\rangle{}} \: . \: {abar}({b}({c}))))]}(\lambda{} dbar_{\left\langle{}\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{},t\right\rangle{}} \: . \: \forall{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{}} \: . \: {dbar}({f}))(\lambda{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{}} \: . \: {f}(\lambda{} x_{e} \: . \: {Cat}({x})))$ |
$=$ | ${[\lambda{} f_{\left\langle{}\left\langle{}\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{},t\right\rangle{},t\right\rangle{}} \: . \: \lambda{} arg_{\left\langle{}\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{},t\right\rangle{}} \: . \: \lambda{} abar_{\left\langle{}e,t\right\rangle{}} \: . \: {f}(\lambda{} b_{\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{}} \: . \: {arg}(\lambda{} c_{\left\langle{}e,t\right\rangle{}} \: . \: {abar}({b}({c}))))]}(\lambda{} dbar_{\left\langle{}\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{},t\right\rangle{}} \: . \: \forall{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{}} \: . \: {dbar}({f}))(\lambda{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{}} \: . \: {f}(\lambda{} x_{e} \: . \: {Cat}({x})))$ |
$=$ | ${[\lambda{} arg_{\left\langle{}\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{},t\right\rangle{}} \: . \: \lambda{} abar_{\left\langle{}e,t\right\rangle{}} \: . \: \forall{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{}} \: . \: {arg}(\lambda{} c_{\left\langle{}e,t\right\rangle{}} \: . \: {abar}({f}({c})))]}(\lambda{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{}} \: . \: {f}(\lambda{} x_{e} \: . \: {Cat}({x})))$ |
$=$ | $\lambda{} abar_{\left\langle{}e,t\right\rangle{}} \: . \: \forall{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{}} \: . \: {[\lambda{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},t\right\rangle{}} \: . \: {f}(\lambda{} x_{e} \: . \: {Cat}({x}))]}(\lambda{} c_{\left\langle{}e,t\right\rangle{}} \: . \: {abar}({f}({c})))$ |
$=$ | $\lambda{} abar_{\left\langle{}e,t\right\rangle{}} \: . \: \forall{} f_{\left\langle{}\left\langle{}e,t\right\rangle{},e\right\rangle{}} \: . \: {abar}({f}(\lambda{} x_{e} \: . \: {Cat}({x})))$ |
It is worth pausing for a moment to contemplate the way this works in the above derivation.
With this framework we can return to our set of inverse linking examples. Note that some of these can be extremely slow to render and we are getting an exponential explosion because CA and CA2 can both apply at any stage; this can be filtered if necessary using the eliminate_dups
function on CompositionResult
s, but it is instructive to see all composition paths so it is not done by default.
tfilter((no * (cat2 * (fromP * france))) * dance2)
r = tfilter((no * (cat2 * (fromP * (a * fcountry)))) * dance2)
r
Three quantifiers:
r[0].tree()
r2 = tfilter((no * (cat2 * (fromP * (a * fcountry)))) * (saw * everyone))
r2
We have:
What is missing?
These are exactly the cases where something would split the scope of the two quantifiers in the subject DP, and empirically, these readings are supposed to be absent in general (this observation is due to May). This demonstrates what Barker calls the Integrity Constraint, which is that when scopal elements form a constituent together, they can't scope independently w.r.t. scopal elements outside that constituent. Intuitively, their scope is compositionally determined inside the smallest constituent they are a member of, and can't be changed or interrupted after that.
Arguably, the derivation of this constraint from extremely general principles is the most important feature of this analysis.
This concludes this fragment; I have left out discussion of conjunction, and of the mathematical parts of Barker's paper.