Lambda Calculus

Abstract:
Now that we've learned something about first-order logic and know how to work with it in Prolog, it is time to have a look at the first major issue of this course, which is:How can we automate the process of associating semantic representations with natural language expressions?In the following lecture we'll explore this issue concretely: first we'll try to write a simple Prolog program that will be able to perform the task we've requested - in a limited way, though. We'll observe where it commits errors, and why. This experiment will seamlessly lead us to formulating a version of λ-calculus. λ-Calculus is a tool that allows us to control the process of making substitutions. With its help, we will be able to describe concisely how semantic representations should be built. It is one of the main tools used in this course, and by the end of the lecture we will have seen why it is so useful to computational semantics.

Table of Contents

Building Meaning Representations
The question we are concerned with now is "Given a sentence of English, how do we get to its meaning representation?" . For our aims, this question is yet far too general, so let's rather ask a more specific one: "Is there a systematic way of translating such simple sentences as John loves Mary and A woman walks into first-order logic?" The important point here is the demand of being systematic. In this section, we will discuss why this is so important.

Syntactic Analysis
For syntactic analysis, we shall use a simple context free grammar. The syntactic analysis of a sentence will be represented as a tree whose non-leaf nodes represent complex syntactic categories (such as S, NP and VP) and whose leaves represent lexical items. Our implementation will make use of Prolog's inbuilt DCG-mechanism.

Semantics Construction
With a little effort, we can do a lot more with DCGs than just parsing sentences: by making use of extra arguments we can associate semantic representations with lexical items quite straightforwardly. The normal Prolog unification mechanism then gives us the basic tool needed to combine semantic representations and to pass them up towards sentence level.We now have a way of handling Task 1: We have used a DCG to specify the syntax of a given fragment of English language. We can even directly use our DCG to parse sentences from this fragment. Thus having prepared the ground, we can now turn to the remaining Tasks 2 and 3. Luckily, we will benefit even here from our choice to use DCGs for Task 1. For with a little effort, we can do a lot more with DCGs than just parsing sentences: by making use of extra arguments we can associate semantic representations with lexical items quite straightforwardly. The normal Prolog unification mechanism then gives us the basic tool needed to combine semantic representations and to pass them up towards sentence level. In short, working with DCGs both dispenses us from having to implement parsers, and at the same time offers us a powerful mechanism for combining representations. So we can devote our attention to semantic construction - and this is what we turn to now. In this section, we approach the matter in a very implementation-centered way. We will supplement our DCG by additional code step by step, wherever we see it is needed in order to produce the results we want to have. Doing so, we will of course not arrive at a principled solution - but we get a nice case study. Looking closely at the problems we have encountered, we will then develop a much cleaner and more general approach in the next lecture.

The Lambda Calculus
In this section we will dicuss a mechanism that gives us a grip on the gaps in templates like the ones making up the complex +-terms that our first approach produced for sentences. We will explicitely mark such gaps and give them names. This will allow us to state precisely how to build a complete first order formula out of a sequence of parts. So we will be able to give a nice and systematic account of the post-processing that used to be done by insertArgs/3 in our naive approach.The naive approach to semantic construction discussed in the previous section became infeasible and very inelegant. But where exactly did we go wrong? As we realized when our sentences became more complex, the problem is that up to now we completely lack a uniform way of combining the collected semantic material into well-formed first order formulae.In this section we introduce λ-calculus, a solution for this problem. For present purposes we shall view it as a notational extension of first order logic that allows us to bind variables using a new variable binding operator λ. Here is a simple λ-expression: λx.WOMAN(x) The prefix λx. binds the occurrence of x in WOMAN(x). It so gives us a handle on this variable, which we can use to state how and when other symbols should be inserted for it.

Implementing Lambda Calculus
Our decision to perform semantic construction with the aid of an abstract "glue" language (namely, λ-calculus) has pleasant consequences for grammar writing, so we would like to make the key combinatorial mechanisms (functional application and β-conversion), available as black boxes to the grammar writer. From a grammar engineering perspective, this is a sensible thing to do: when writing fragments we should be free to concentrate on linguistic issues. In this Section we build the required black box. In this section we build a "black box" making available the key combinatorial mechanisms required for working with λ-calculus: functional application and β-conversion. Once we have such a black box available, we will be able to decorate our little DCG from » A First Attempt with extremely natural semantic construction code and start building representations. We will do this immediately in the rest of the section, and experiment with the resulting system.As a testbed for our β-conversion mechanism, the rank-grown implementation of our little natural language processing system will do. But afterwards, we shall take a step back, look at it again and re-structure it from the perspective of software engineering.

Running the Program
Driver Predicate and Code Summary for this Chapter.


Exercise

  1. Add a clause to insertArgs/2 (see » Putting Things in the Right Place I) in firstAttempt.pl that handles quantified sentences like A man walks.
    [Hint: If you follow the pattern of the clauses of insertArgs/2 already given, you may run into trouble with your new clause since terms like exists(_A,_B&_C)+man(_D)+walk(_E) also match on one of the first two clauses. You may either catch this inside the respective clause or simply put your clause above the other clauses.]
  2. Look at the semantics construction in » Advanced Topics: Proper Names and Transitive Verbs again. Work through the functional applications and β-reductions required to build the VP and S representations. Make sure you understand the role-reversing idea used in the TV semantic representation.
  3. Starting off from our treatment of transitive verbs (» Advanced Topics: Proper Names and Transitive Verbs), how would you handle ditransitive verbs such as offer? Give a semantic representation for offer, as well as semantically annotated grammar rules to analyse sentences like Mary offers John a siamese cat.
  4. Find a suitable λ-expression for the determiner no and add it to our implementation of λ-calculus. Test your solution with sentences like No man walks.
    Extend our implementation of λ-based semantic construction accordingly.
  5. [This is a mid-term project]

    betaConversion.pl: View_Download

    We could prevent accidental variable bindings as shown in » An Afterthought on Alpha-Conversion once and for all if we had a module doing α-conversion.
    Design such a module and include it as a preprocessing step in a new driver predicate for β-conversion:
    
     alphaBetaConvert(F@A,Result):-
    
        alphaConvert(F,ConvertedF),
    
        betaConvert(ConvertedF@A,Result).
    
    Please comment your code and provide a short documentation. The documentation should include a brief description of the problem and of your solution.
    Test your module on some examples and include your results in the documentation. Does your program support the use of Prolog atoms to represent first order variables (i.e could you for instance write lambda(x walks(x)) instead of lambda(X walks(X)))?
    Please contact us if you would like to do this exercise as your mid-term project.