Skip to content

TRALE: Goals and Macros

TRALE is a programming language, and you can define functions as goals and macros.

Table of Contents

Open Table of Contents

1. Goals

Goals in TRALE resemble logical functions and are defined using the keyword if. A goal is true if all of its conditions are true. Goals can be composed of other goals, creating a hierarchy of logical conditions.

Syntax for Defining Goals

The syntax for goals is as follows:

goal_name(Arg1, Arg2, ...) if true.
goal_name(Arg1, Arg2, ...) if
    condition1(...),
    condition2(...).

Example: A Simple Goal

Consider a scenario where we define a goal to check if someone is a good student. In TRALE, you could define it as:

good_student(X) if
    student(X),
    taking_csc485(X).

In this case:

Example Usage

If we have defined the following facts:

student(frank) if true.
taking_csc485(frank) if true.

professor(gerald) if true.
taking_csc485(gerald) if true.

The goal good_student(frank) would return true because Frank satisfies both conditions of being a student and taking CSC485:

?- good_student(frank).
true.

However, Gerald would not be considered a good student, since he is a professor, not a student:

?- good_student(gerald).
false.

Example: Recursive Goals

Goals in TRALE can also be recursive, making them more powerful and flexible. A common example is defining an append goal that appends two lists.

append([], Xs, Xs) if true.
append([H|T1], L2, [H|T2]) if append(T1, L2, T2).

In this example:

Equivalent Python Code

To help visualize, here’s a Python equivalent of the append goal:

def append(l1, l2):
    if l1 == []:
        return l2
    else:
        h, *t1 = l1
        t2 = append(t1, l2)
        return [h] + t2

Using Goals in Rules

Goals can be used within TRALE rules to impose constraints or enforce conditions. For example, let’s ensure that the second object of a verb phrase (VP) is not a pronoun. This can be expressed with the following goal:

% pronoun_limit(Object, SubCatRest)
pronoun_limit(sem:book, [_]) if true.
pronoun_limit(sem:Sem, [_|[_]]) if true.

Here, the first case states that if the rest of the subcat list has only one element (e.g., the subject), the object must have the semantic book. The second case allows the object to be anything if there are two elements (e.g., Obj2 and the subject).

Applying Goals in Rules

To use this goal in a VP rule:

vp rule
(vp, agr:Agr, subcat:(Rest, [_|_])) ===>
cat> (verbal, agr:Agr, subcat:[Obj|Rest]),
cat> Obj,
goal> pronoun_limit(Obj, Rest).

This rule ensures that the object in the verb phrase conforms to the pronoun_limit goal.

Example:

Given the sentence “I give him books”:

| ?- rec[i, give, him, books].

The result is valid:

STRING: 
0 i 1 give 2 him 3 books 4
ANOTHER?  y

However, for “I give books him”, the order is incorrect, and the parser rejects it:

| ?- rec[i, give, books, him].
STRING: 
0 i 1 give 2 books 3 him 4
no

2. Macros

Macros in TRALE act as shorthand for commonly used structures, making complex expressions simpler to write and maintain. They are especially useful when working with repeated patterns or nested feature structures.

Syntax for Macros

You can define a macro using the following syntax:

macro_name(Arg1, Arg2, ...) macro (type, arg1:Arg1, ...).

To call the macro, use the @ symbol:

@macro_name(Arg1, Arg2, ...)

This call expands into the structure defined by the macro.

Example: Logic Form Notation

Consider a logic form with lambda calculus notation. Without macros, the structure is verbose:

(lambda, bind:X, body:F)
(forall, bind:X, body:(imply, lhs:F, rhs:P))

Using macros, we can simplify this:

lambda(X, Body) macro (lambda, bind:X, body:Body).
forall(X, Restr, Body) macro (forall, bind:X, body:(imply, lhs:Restr, rhs:Body)).
exists(X, Restr, Body) macro (exists, bind:X, body:(and, lhs:Restr, rhs:Body)).
apply(F, Args) macro (app, f:F, args:Args).

Example Usage of Macros

Using the macros, the statement:

λx.y.(F(x,y)P(x,y))\lambda x. \forall y. (F(x, y) \Rightarrow P(x, y))

can be expressed as:

@lambda(X,
    @forall(Y,
        @apply(F, [X, Y]),
        @apply(P, [X, Y])))

This is much more readable and easier to write than manually constructing the entire feature structure each time.