Getting started with FCG

Authors: Paul Van Eecke and Katrien Beuls

This technical recipe describes the notational conventions for the current implementation of Fluid Construction Grammar (FCG). You can also follow the definitions given here below by loading the fcg-manual.lisp file in the tutorial subfolder of your Babel installation. The notation is based on the formalisation of FCG described in Steels (2017). It focuses on the implementation and therefore assumes you have read this paper. For any questions, do not hesitate to contact us at ehai@ai.vub.ac.be.

Steels, Luc (2017). Basics of Fluid Construction Grammar. Constructions and Frames 9(2).
Web demo: https://www.fcg-net.org/demos/basics-of-fcg/.

Download, Installation and Start-up

Fluid Construction Grammar is released as a part of the Babel toolkit and runs on Mac OS, Linux and Windows. You can get Babel here. Installation instructions can be found here.

After having successfully installed Babel, you can use this manual in an interactive way. Evaluate the following lines to load the FCG package and to start the web-interface. Then, open a web browser (preferably Safari or Firefox) at http://localhost:8000.

(ql:quickload :fcg)
(in-package :fcg)
(activate-monitor trace-fcg)

The basics of FCG

Defining a construction inventory

Before you can start to write your own FCG grammar, you will have to define a construction-inventory. This is the place where all constructions will be stored. A new construction- inventory can be defined in its simplest form by executing the following macro. You will see that it is initially empty (0 constructions). The construction-inventory is stored by default in the local variable *fcg-constructions*.

(def-fcg-constructions my-first-grammar)

Defining a construction

Now that we have defined a construction-inventory, we can define our first construction. The easiest way to do this is by using the def-fcg-cxn macro. This macro takes the construction name as first argument (mouse-cxn in the example below) and a representation of the construction as a second argument. The representation of the construction consists of two parts. In the upper part, the contributing units (= left-hand-side of a construction in Steels (2017)) of the construction are defined. In the following mouse-cxn, there is only one contributing unit, namely ?mouse-unit.

Then, the arrow separating the contributing part and the conditional part of the construction is given by the symbol <-. Under the arrow, the conditional part of the construction (= Right-hand-side) of the construction in Steels (2017)) is declared. The conditional part can at its turn consists of different conditional units (but our example construction has only one). Every conditional unit consists of the unit name and then two parts, the formulation-lock and the comprehension-lock. The formulation lock is given immediately after the unit-name and is followed by the symbol --. Then, the comprehension lock is declared and the unit is closed.

For requiring a feature to be present in the input and for relocating it to an other unit, you can put HASH in front of the feature name. This corresponds to the # symbol used in Steels (2017).

Now you can evaluate the following mouse-cxn. It will be added to the construction-inventory that we have defined above.

(def-fcg-cxn mouse-cxn
             ((?mouse-unit
               (args ?x)
               (syn-cat (class noun))
               (sem-cat (sem-type physical-entity)))
              <-
              (?mouse-unit
               (HASH meaning ((mouse ?x)))
               --
               (HASH form ((string ?mouse-unit "mouse"))))))

The three faces of FCG

There exist three representation forms for FCG constructions. First of all, there is the ‘Lisp notation’ used in this manual. This is the ‘base notation’ that actually runs. Second, there is the interactive web-interface representation, which is automatically created based on the ‘Lisp notation’. Third, there is the LaTeX notation used in FCG papers and slideshows. The LaTeX notation is also directly translated from the Lisp notation.

Let’s inspect the mouse-cxn that we have just defined in the other representations. If you go to your web browser (which should still be at https://localhost:8000), you will see that it displays the fact that you added 1 construction to the construction inventory. Click on the blue box with ‘mouse-cxn’ in order to expand it. You will see the the contributing part on the left side, the arrow, and the conditional part on the right side, showing all the units in the construction. Now click on ‘expand’ (or on the units one by one). You will see all the features in the units nicely formatted, and the two locks in the conditional units.

If you hover over the name of a construction, some symbols appear above it. If you click the L symbol, a file with the LaTeX source code for this construction will automatically download. Paste this code into an existing LaTeX document, compile, and you will see the construction in its LaTeX notation.

Using a grammar in comprehension and formulation

Now that we have defined a construction inventory and one construction, it is time to add some more constructions and use them for the very thing FCG was implemented for: processing. Evaluate the following to simple constructions. They will be added to the same construction inventory which you defined above.

(def-fcg-cxn the-cxn
             ((?the-unit
               (args ?x)
               (syn-cat (class quantifier))
               (sem-cat (sem-type referent)))
              <-
              (?the-unit
               (HASH meaning ((unique ?x)))
               --
               (HASH form ((string ?the-unit "the"))))))

(def-fcg-cxn np-cxn
             ((?np-unit
               (args ?x)
               (syn-cat (class referring-expression))
               (subunits (?mouse-unit ?the-unit)))
              <-
              (?the-unit
               (sem-cat (sem-type referent))
               (args ?x)
               --
               (syn-cat (class quantifier)))
              (?mouse-unit
               (sem-cat (sem-type physical-entity))
               (args ?x)
               --
               (syn-cat (class noun)))
              (?np-unit
               --
               (HASH form ((meets ?the-unit ?mouse-unit))))))

We can now use the ‘mouse-cxn’ ‘the-cxn’ and ‘np-cxn in processing’. FCG is a bidirectional formalism in the sense that it uses the same constructions and processing mechanisms for both directions. Evaluate the following lines for testing the processing. Look at the web interface for seeing the construction application process and results. For clarity reasons, it might be a good idea to refresh your web browser every between two construction applications.

;; In comprehension
(comprehend '("the" "mouse") :cxn-inventory *fcg-constructions*)
(comprehend '("mouse" "the"))

;; In formulation
(formulate '((mouse obj) (unique obj)))

Apart from comprehend and formulate which return the first solution they found, there exist also comprehend-all and formulate-all, which examine the whole search tree and return all solutions. For the tiny grammar above, these will make no difference, as there is only one solution.

It can be very informative to inspect the construction application process in the web-interface. Click two times (don’t ‘double click’) on one of the nodes in the construction application path (green). Now, you can see the transient structure before the application of this construction, the applied construction, and the transient structure after the application. Expand/minimize units and nodes by clicking on them.

Feature Types

Linguistic information is represented using feature-value pairs. Such pairs consist of a feature name (or attribute) and a value for that feature. In bracketed notation, this looks as follows:

(feature-name value)

The FCG-system is an open-ended system that lets you decide yourself which features are relevant for the language that you’re describing and which values they can take. That is, there are no linguistic type restrictions on feature value pairs. Moreover, the feature-names and values are ‘arbitrary’ in the sense that they do not have any meaning outside the grammar. In other terms, the way in which they are used inside one grammar is their only meaning.

However there are formal type restrictions on feature-values that affect the way that feature behaves during processing (the way that features are matched and merged). The value types that are implemented are:

  • Sequence
  • Set
  • Set of predicates
  • All the rest that does not fit in the above categories

Sequence

Sequences are ordered lists. For example, you can imagine a feature called word-form whose value is a list of ordered strings. For instance:

(word-form ("song" "-s"))
(word-form ("song" "zero-marking"))

When such a feature-value pair occurs in a formulation or comprehension lock, the construction specifies that it can only contribute its information if an “exact pattern match” can be found in the transient structure. We can manually test this using the function test-match, which takes the value from a construction’s lock as its first argument, and a potential match from the transient structure as its second value:

(test-match ("song" "-s") ("song" "-s") :type sequence)

When evaluating this, the result is a list of hypotheses (see further below). In our example, the return value is (((T . T))), which is a list that contains one solution: ((T . T)), which means that FCG’s unification algorithm found an exact match. The following test fails because the two values do not match:

(test-match ("song" "-s") ("song" "zero-marking") :type sequence)

In other words, if a construction contains the following information in one of its locks:

(word-form ("song" "-s"))

It can only be unlocked if a match for that value is found in the transient structure.

Variables and variable substitution

Often, a construction will under-specify information. For example, the plural-s construction may not care about the first string in the value of the feature word-form. In this case, we can use variables to under-specify this information. Variables are symbols that start with a question mark. So the plural-s form could contain the following feature-value pair:

(word-form (?stem "-s"))

Let’s try to match value that against several other values:

(test-match (?stem "-s") ("song" "-s") :type sequence)
(test-match (?stem "-s") ("car" "-s") :type sequence)
(test-match (?stem "-s") ("song" "zero-marking") :type sequence)

The result value of the first test was (((?stem . "song"))). This means that the unification algorithm found one solution: ((?stem . "song")). This result means that if we substitute the variable ?stem for the value "song", we would get an exact match with the value of the transient structure:

(?stem "-s")  ----> substitute ?stem with "song" ----> ("song" "-s")

Similarly, in the second example, we get an exact match between the two values if we substitute ?stem for the value "car". The third example fails, however, because we cannot find any substitution for the variable ?stem that would make the two values the same.

Variable equalities

Variables can be assigned any value, but once a value has been assigned to them, this value needs to be consistently used throughout the construction. That is, if the same variable name occurs more than once, it must always be bound to the same value.

Example: suppose that we want to describe Pingelapese, a Micronesian language that features duplication and triplication. For instance, the word “mejr” (to sleep) can be reduplicated into “mejrmejr” (sleeping) and triplicated into “mejrmejrmejr” (still sleeping). We can imagine a feature called duplicated form that looks as follows: (duplicated-form ("mejr" "mejr")). The duplication-construction, however, should abstract away from the specific form and be compatible with any word that allows duplication. This can be achieved through variable equalities: (duplicated-form (?form ?form)) .

(test-match (?form ?form) ("mejr" "mejr") :type sequence) ;; succeeds
(test-match (?form ?form) ("ha" "ha") :type sequence) ;; succeeds
(test-match (?form ?form) ("song" "-s") :type sequence) ;; fails

Variable bindings

While processing, the FCG-system will often have already found a binding for a variable. Suppose that the duplication construction contains the following two feature-value pairs:

(base-form ?base-form)
(duplicated-form (?base-form ?base-form))

Then it is possible that the unification algorithm has already found the following bindings:

(?base-form . "mejr")

We can now exploit these bindings for restricting the search for good matches:

(test-match (?base-form ?base-form) ("mejr" "mejr") :type sequence
            :bindings ((?base-form . "mejr"))) ;; Succeeds.
(test-match (?base-form ?base-form) ("mejr" "mejr") :type sequence
            :bindings ((?base-form . "song"))) ;; Fails because inconsistent.

A powerful usage of variables is that they can be bound to other variables, which allows information to remain under-specified until a value is found:

(test-match (?stem ?suffix) ("song" ?morpheme) :type sequence)

Sequences in the contributing

When sequences occur in the contributing part of a construction, the FCG-system will try to “merge” that information with information already present in the transient structure. In the simplest case, this simply means adding the information:

(test-merge ("song" "-s") () :type sequence)

A second simple case is that the transient structure already contains the information. In this case, merging needs to be compatible with that information. Just like with the conditional part, variables may be used.

(test-merge ("song" "-s") ("song" "-s") :type sequence) ;; Succeeds because values are the same.
(test-merge (?stem "-s") ("song" "-s") :type sequence) ;; Succeeds because ?stem can be substituted.
(test-merge (?stem "-s") ("song" "zero-marking") :type sequence) ;; Fails because information conflicts.

Sometimes, however, the information in the transient structure may be incomplete. In this case, merging will only succeed if the information in the transient structure can be changed in a way that makes it compatible with the information supplied by the construction. In this way, the plural-s construction may add the “-s” string:

(test-merge (?stem "-s") ("song") :type sequence)

Or the duplication construction may duplicate the single form of a basic lexical entry:

(test-merge (?base-form ?base-form) ("mejr") :type sequence)

Conflicting information, e.g. when the order is violated, will lead to failure:

(test-merge (?stem "-s") ("-s" "song") :type sequence)

Also be careful with variables, because they can be bound to any value:

(test-merge (?stem "-s") ("-s") :type sequence)

Set and set-of-predicates

The behavior of the unification algorithm changes to some kind of “subset” operation if a feature-value is typed as a set or set-of-predicates. For instance, suppose that we want represent the meaning of verbs according to Adele Goldberg’s 1995 book “Constructions: A constructional approach to argument structure”. Goldberg writes that a verb comes with a participant structure. For instance, a “buy”-event involves a “buyer”, a “seller” and something that is being “bought”. We can use a first order predicate calculus for representing that semantic frame: buy(?ev), buyer(?ev, ?x), seller(?ev, ?y), bought(?ev, ?z). In bracketed notation, this becomes:

 ((buy ?ev) (buyer ?ev ?x) (seller ?ev ?y) (bought ?ev ?z))

The meaning of the utterance “Sally bought a book from Pat” could thus be represented as follows:

((sally a) (pat b) (book c)
(buy ev-1) (buyer ev-1 a) (seller ev-1 b) (bought ev-1 c))

A lexical construction, such as the book-construction, only needs to cover its own meaning and does not care about other meanings that the speaker may want to express. In this case, we do not want the unification algorithm to search for an exact match in the transient structure, but to find a subset of the value in the transient structure that matches with the conditional value of the construction:

(test-match ((book ?x)) ((sally a) (pat b) (book c)) :type set-of-predicates)

This succeeds because the unification finds that ((book ?x)) can be matched against the subset ((book c)). The two values are equal if we substitute ?x for the value c. Also order does not matter anymore:

(test-match ((book ?x) (sally ?y)) ((sally a) (pat b) (book c)) :type set-of-predicates)

Of course, variable equalities do matter:

(test-match ((book ?x) (sally ?x)) ((sally a) (pat b) (book c)) :type set-of-predicates)

Beware of variables in sets

Sets and set-of-predicates change to unification algorithm to its most lenient behavior. You should therefore be very careful with the use of variables!

(test-match (?x) ((sally a) (pat b) (book c)) :type set-of-predicates) ;; 3 hypotheses
(test-match (?x ?y) ((sally a) (pat b) (book c)) :type set-of-predicates) ;; 6 hypotheses

Contributable information

(test-merge ((book ?X)) () :type set-of-predicates) ;; Succeeds
(test-merge ((book ?X)) ((book x)) :type set-of-predicates) ;; Succeeds
(test-merge ((book ?x)) ((sally y)) :type set-of-predicates) ;; Succeeds

Does the following succeed? Why (not)?

(test-merge ((book ?x)) ((book a)) :type set-of-predicates :bindings ((?x . b)))

All the rest…

Most features, however, are best handled by the unification algorithm’s default behavior (subset). This also means that you do not explicitly have to type them. This is especially useful for features whose values are complex, e.g. values that are feature-value pairs themselves.

For example, the value agreement consists of a list of feature-value pairs themselves (e.g. number and person): (agreement (number sg) (person 3))

(test-match ((number sg) (person 3)) ((number sg) (person 3)))
(test-match ((number ?n) (person ?p)) ((number sg) (person 3)))
(test-match ?agreement ((number sg) (person 3))) ;; Succeeds!! Why??

The difference (and utility) of the default behavior with respect to sets becomes clear in merging. For example, if the value of the agreement feature is considered as a set, then the following unwanted behavior may occur:

(test-merge ((number sg) (person 3)) ((number pl) (person 3)) :type set) ;; succeeds (unwanted!)

In the default behavior, every element in a feature’s value may only occur once in the transient structure’s feature-value pair:

(test-merge ((number sg) (person 3)) ((number pl) (person 3))) ;; fails (as wanted)