<< Tutorial 3 ^^Contents

Yield Prolog Tutorial

4. Compiler

In Tutorial 4, we work with the parser, compiler and module system. (Since this is for compiling Prolog code, this assumes some familiarity with the Prolog syntax.)

Dynamic Assert and Match

Let's modify the "brother" example from Tutorial 2 by creating it dynamically.

Python
C#
    YP.assertFact(Atom.a("brother"), \
[Atom.a("Hillary"), Atom.a("Hugh")])
YP.assertFact(Atom.a("brother"), \
[Atom.a("Hillary"), Atom.a("Tony")])
YP.assertFact(Atom.a("brother"), \
[Atom.a("Bill"), Atom.a("Roger")])

Brother = Variable()
print("Using dynamic assert:")
for l1 in YP.matchDynamic \
(Atom.a("brother"), \
[Atom.a("Hillary"), Brother]):
print("Hillary has brother " +
str(Brother.getValue()))
using System;
using System.Collections.Generic;
using YieldProlog;
class Tutorial4
{
static void Main(string[] args)
{
YP.assertFact(Atom.a("brother"),
new object[] { Atom.a("Hillary"), Atom.a("Hugh") });
YP.assertFact(Atom.a("brother"),
new object[] { Atom.a("Hillary"), Atom.a("Tony") });
YP.assertFact(Atom.a("brother"),
new object[] { Atom.a("Bill"), Atom.a("Roger") });

Variable Brother = new Variable();
Console.WriteLine("Using dynamic assert:");
foreach (bool l1 in YP.matchDynamic
(Atom.a("brother"), new object[]
{ Atom.a("Hillary"), Brother}))
Console.WriteLine("Hillary has brother " +
Brother.getValue() + ".");
}
}
Javascript
    YP.assertFact(Atom.a("brother"), 
[Atom.a("Hillary"), Atom.a("Hugh")]);
YP.assertFact(Atom.a("brother"),
[Atom.a("Hillary"), Atom.a("Tony")]);
YP.assertFact(Atom.a("brother"),
[Atom.a("Bill"), Atom.a("Roger")]);

var Brother = new Variable();
document.write("Using dynamic assert:<br>");
for (let l1 of YP.matchDynamic
(Atom.a("brother"),
[Atom.a("Hillary"), Brother]))
document.write("Hillary has brother " +
Brother.getValue() + ".<br>");


There are a few differences from the earlier example:
This prints the same result as before:
Using dynamic assert:
Hillary has brother Hugh.
Hillary has brother Tony.

Dynamic vs. static

In this tutorial, we want to learn to use the compiler which produces static functions, so why are we learning about dynamic matching? Because to use the compiler properly, we need to control when the compiler will try to call a function statically or dynamically. For example, let us repeat the earlier static definition of parent:

Python C#
def parent(arg1, arg2):
for l1 in YP.unify \
(arg1, Atom.a("Chelsea")):
for l2 in YP.unify \
(arg2, Atom.a("Hillary")):
yield False
for l1 in YP.unify \
(arg1, Atom.a("Chelsea")):
for l2 in YP.unify \
(arg2, Atom.a("Bill")):
yield False
using System;
using System.Collections.Generic;
using YieldProlog;
class Tutorial4
{
public static IEnumerable<bool> parent
(object Person, object Parent) {
foreach (bool l1 in YP.unify
(Person, Atom.a("Chelsea"))) {
foreach (bool l2 in YP.unify
(Parent, Atom.a("Hillary")))
yield return false;
}
foreach (bool l1 in YP.unify
(Person, Atom.a("Chelsea"))) {
foreach (bool l2 in YP.unify
(Parent, Atom.a("Bill")))
yield return false;
}
}
}
Javascript
function* parent(Person, Parent) {
for (let l2 of YP.unify
(Person, Atom.a("Chelsea"))) {
for (let l3 of YP.unify
(Parent, Atom.a("Hillary"))) {
yield false;
}
}
for (let l2 of YP.unify
(Person, Atom.a("Chelsea"))) {
for (let l3 of YP.unify
(Parent, Atom.a("Bill"))) {
yield false;
}
}
}

When we compile uncle, which calls parent and brother, how will it know which is dynamic or static?

Compiling uncle

Here again is the Prolog code for uncle, which we want to parse and compile:
uncle(Person, Uncle) :-
parent(Person, Parent),
brother(Parent, Uncle).

Python C#
    print "# Compiled code:"
prologCode = \
"uncle(Person, Uncle) :- \n" + \
" parent(Person, Parent), \n" + \
" brother(Parent, Uncle). \n"
YP.tell(sys.stdout)
YP.see(YP.StringReader(prologCode))
TermList = Variable()
PseudoCode = Variable()
for l1 in parseInput(TermList):
for l2 in makeFunctionPseudoCode \
(TermList, PseudoCode):
convertFunctionPython(PseudoCode)
YP.seen()
using System;
using System.Collections.Generic;
using YieldProlog;
class Tutorial4
{
static void Main(string[] args)
{
Console.WriteLine("// Compiled code:");
string prologCode =
"uncle(Person, Uncle) :- \n" +
" parent(Person, Parent), \n" +
" brother(Parent, Uncle). \n";
YP.tell(Console.Out);
YP.see(new StringReader(prologCode));
Variable TermList = new Variable();
Variable PseudoCode = new Variable();
foreach (bool l1 in Parser.parseInput(TermList)) {
foreach (bool l2 in Compiler.makeFunctionPseudoCode
(TermList, PseudoCode))
Compiler.convertFunctionCSharp(PseudoCode);
}
YP.seen();
}
}
Javascript
    var prologCode =
"uncle(Person, Uncle) :- \n" +
" parent(Person, Parent), \n" +
" brother(Parent, Uncle). \n";
YP.see(new YP.StringReader(prologCode));
var output = new YP.StringWriter();
YP.tell(output);
var TermList = new Variable();
var PseudoCode = new Variable();
for (let l1 of parseInput(TermList)) {
for (let l2 of makeFunctionPseudoCode
(TermList, PseudoCode))
convertFunctionJavascript(PseudoCode);
}
YP.seen();
YP.told();
document.write("// Compiled code:<br>");
document.write
(output.toString().replace
(/\n/g,"<br>").replace(/ /g,"&nbsp;"));


This parse and compile code follows these steps:
  1. Use YP.see to set the default input for the parser, in this case to read from the prologCode string. The input must end with a newline.
  2. Use YP.tell to set the default output for the compiler.
  3. Call parseInput to read from the default input and output the TermList.
  4. Call makeFunctionPseudoCode to convert the TermList to PseudoCode, which is an intermediate format which can be converted to several languages. This yields multiple times for the pseudo code for each function we want to output.
  5. Call convertFunctionPython, convertFunctionJavascript or convertFunctionCSharp as appropriate for the target language. This writes to the default output.
Here is the compiled output for each target language:

Python C#
# Compiled code:
def getDeclaringClass():
return globals()

def uncle(Person, Uncle):
Parent = Variable()
for l1 in YP.matchDynamic \
(Atom.a("parent"), [Person, Parent]):
for l2 in YP.matchDynamic \
(Atom.a("brother"), [Parent, Uncle]):
yield False

// Compiled code:
public class YPInnerClass {}
public static Type getDeclaringClass() {
return typeof(YPInnerClass).DeclaringType;
}

public static IEnumerable<bool> uncle
(object Person, object Uncle) {
{
Variable Parent = new Variable();
foreach (bool l2 in YP.matchDynamic
(Atom.a("parent"), new object[]
{Person, Parent})) {
foreach (bool l3 in YP.matchDynamic
(Atom.a("brother"), new object[]
{Parent, Uncle})) {
yield return false;
}
}
}
}
Javascript
// Compiled code:
function getDeclaringClass() { return null; }

function* uncle(Person, Uncle) {
  {
    var Parent = new Variable();
    for (let l2 of YP.matchDynamic
(Atom.a("parent"),
[Person, Parent])) {
      for (let l3 of YP.matchDynamic
(Atom.a("brother"),
[Parent, Uncle])) {
        yield false;
      }
    }
  }
}

(We will discuss getDeclaringClass later.) Notice that in the uncle function, the compiler used YP.matchDynamic to call both parent and brother. This is what we want for brother, but we want to call parent statically. There are three ways in which the compiler will call a function statically:
  1. Use import in the Prolog code
  2. Define the function locally in the same input file
  3. Use module Atom.a("") when calling as a dynamic goal
We look at each of these below.

Using import to call a static function

If we want to call a static function [3], but it is not defined in the Prolog code, we can simply add an import directive. (In Prolog, if you start a line with :- it is a directive to the compiler. Don't forget to end with a period.):
:- import('', [parent/2]).

uncle(Person, Uncle) :-
parent(Person, Parent),
brother(Parent, Uncle).
We use this as the prologCode string in the same parse and compile code as above. Here is the output:

Python C#
# Calling an imported function:
def getDeclaringClass():
return globals()

def uncle(Person, Uncle):
Parent = Variable()
for l1 in parent(Person, Parent):
for l2 in YP.matchDynamic \
(Atom.a("brother"), [Parent, Uncle]):
yield False
// Calling an imported function:
public class YPInnerClass {}
public static Type getDeclaringClass() {
return typeof(YPInnerClass).DeclaringType;
}

public static IEnumerable<bool> uncle
(object Person, object Uncle) {
{
Variable Parent = new Variable();
foreach (bool l2 in parent(Person, Parent)) {
foreach (bool l3 in YP.matchDynamic
(Atom.a("brother"), new object[]
{Parent, Uncle})) {
yield return false;
}
}
}
}
Javascript
// Calling an imported function:
function getDeclaringClass() { return null; }

function* uncle(Person, Uncle) {
  {
    var Parent = new Variable();
    for (let l2 of parent(Person, Parent)) {
      for (let l3 of YP.matchDynamic
(Atom.a("brother"),
[Parent, Uncle])) {
        yield false;
      }
    }
  }
}

Notice that parent is called statically.

The import directive has two arguments. The first argument is the module where the imported function is found, which is always ''. [4] For C#, this means the imported function is in the same class as the calling function. For Javascript and Python, this means the imported function is in the global scope.

The second argument to import is the comma-separated list of imported functions, where each member of the list is name/n, where name is the name of the function and n is the number of arguments. In this example, parent has two arguments, so we use parent/2.

Calling local functions statically

Instead of using import, a way to get the same result is to define parent in the same input with uncle:
parent('Chelsea', 'Hillary').
parent('Chelsea', 'Bill').

uncle(Person, Uncle) :-
parent(Person, Parent),
brother(Parent, Uncle).
We use this as the prologCode string in the same parse and compile code as above. Here is the output:

Python C#
# Calling a locally-defined function:
def getDeclaringClass():
return globals()

def parent(arg1, arg2):
for l1 in YP.unify(arg1, Atom.a("Chelsea")):
for l2 in YP.unify(arg2, Atom.a("Hillary")):
yield False
for l1 in YP.unify(arg1, Atom.a("Chelsea")):
for l2 in YP.unify(arg2, Atom.a("Bill")):
yield False

def uncle(Person, Uncle):
Parent = Variable()
for l1 in parent(Person, Parent):
for l2 in YP.matchDynamic \
(Atom.a("brother"), [Parent, Uncle]):
yield False
// Calling a locally-defined function:
public class YPInnerClass {}
public static Type getDeclaringClass() {
return typeof(YPInnerClass).DeclaringType;
}

public static IEnumerable<bool> parent
(object arg1, object arg2) {
{
foreach (bool l2 in YP.unify
(arg1, Atom.a("Chelsea"))) {
foreach (bool l3 in YP.unify
(arg2, Atom.a("Hillary"))) {
yield return false;
}
}
}
{
foreach (bool l2 in YP.unify
(arg1, Atom.a("Chelsea"))) {
foreach (bool l3 in YP.unify
(arg2, Atom.a("Bill"))) {
yield return false;
}
}
}
}

public static IEnumerable<bool> uncle(object Person, object Uncle) {
{
Variable Parent = new Variable();
foreach (bool l2 in parent(Person, Parent)) {
foreach (bool l3 in YP.matchDynamic
(Atom.a("brother"), new object[]
{Parent, Uncle})) {
yield return false;
}
}
}
}
Javascript
// Calling a locally-defined function:
function getDeclaringClass() { return null; }

function* parent(arg1, arg2) {
  {
    for (let l2 of YP.unify
(arg1, Atom.a("Chelsea"))) {
      for (let l3 of YP.unify
(arg2, Atom.a("Hillary"))) {
        yield false;
      }
    }
  }
  {
    for (let l2 of YP.unify
(arg1, Atom.a("Chelsea"))) {
      for (let l3 of YP.unify
(arg2, Atom.a("Bill"))) {
        yield false;
      }
    }
  }
}

function* uncle(Person, Uncle) {
  {
    var Parent = new Variable();
    for (let l2 of parent(Person, Parent)) {
      for (let l3 of YP.matchDynamic
(Atom.a("brother"),
[Parent, Uncle])) {
        yield false;
      }
    }
  }
}

Notice that the compiled version of parent is basically the same as the hand-coded one above (with different argument names). Also note that in uncle, parent is called statically because it is defined in the same input code.

In summary, it is preferred to put all the static functions in the same input file since all functions need to be in the same class (for C#) or in the global scope (for Python and Javascript). However, if you want to hand-code a static function, or don't have all functions available when you call parseInput, then use the import directive to statically call them. This covers most cases, but we still need to look at dynamic goals.

Module Atom.a("") for dynamic goals

Prolog allows you to define and call code dynamically. For example, let us modify uncle to put parent inside a dynamic goal and then call the goal:
:- import('', [parent/2]).

uncle(Person, Uncle) :-
Goal = parent(Person, Parent),
Goal,
brother(Parent, Uncle).
We use this as the prologCode string in the same parse and compile code as above. Here is the output:

Python C#
# Calling a dynamic goal:
def getDeclaringClass():
return globals()

def uncle(Person, Uncle):
Goal = Variable()
Parent = Variable()
for l1 in YP.unify \
(Goal, Functor2 \
(Atom.a("parent", Atom.a("")), \
Person, Parent)):
for l2 in YP.getIterator \
(Goal, getDeclaringClass()):
for l3 in YP.matchDynamic \
(Atom.a("brother"), [Parent, Uncle]):
yield False
// Calling a dynamic goal:
public class YPInnerClass {}
public static Type getDeclaringClass() {
return typeof(YPInnerClass).DeclaringType;
}

public static IEnumerable<bool> uncle
(object Person, object Uncle) {
{
Variable Goal = new Variable();
Variable Parent = new Variable();
foreach (bool l2 in YP.unify
(Goal, new Functor2
(Atom.a("parent", Atom.a("")),
Person, Parent))) {
foreach (bool l3 in YP.getIterator
(Goal, getDeclaringClass())) {
foreach (bool l4 in YP.matchDynamic
(Atom.a("brother"), new object[]
{Parent, Uncle})) {
yield return false;
}
}
}
 }
}
Javascript
// Calling a dynamic goal:
function getDeclaringClass() { return null; }

function* uncle(Person, Uncle) {
  {
    var Goal = new Variable();
    var Parent = new Variable();
    for (let l2 of YP.unify
(Goal, new Functor2
(Atom.a("parent", Atom.a("")),
Person, Parent))) {
      for (let l3 of YP.getIterator
(Goal, getDeclaringClass())) {
        for (let l4 of YP.matchDynamic
(Atom.a("brother"),
[Parent, Uncle])) {
          yield false;
        }
      }
    }
  }
}

In uncle, we first unify Goal with a Functor2 which represents the term parent(Person, Parent). Notice that  Atom.a("parent", Atom.a("")) has a second argument which is the module that declared the Atom. (Atom.a("") is the same as the Prolog code for the module '' in the import directive.) As before, the declaring module is Atom.a("") if the function is in the import directive, or if a function with the atom's name is defined locally in the same input.

Next, we pass the Goal to YP.getIterator which examines the goal and figures out how to call it. YP.getIterator sees that it needs to call parent and that the Atom for parent was declared in the module Atom.a("") which means "the same module as the caller". This is why the caller always passes getDeclaringClass(). For Python and Javascript, getDeclaringClass returns null which means the global scope. For C#, getDeclaringClass uses a trick with the locally-defined YPInnerClass to get the Type object of the class of the caller. In this way, YP.getIterator finds and calls the statically defined parent function. (If the Atom for the function name is defined without a module or with the module Atom.NIL, then YP.getIterator uses YP.matchDynamic.)

In summary, you will rarely have to write code which directly calls YP.getIterator or getDeclaringClass. But it is important to understand how static functions are called from dynamic goals, especially if a dynamic goal is passed as an argument from one function to another before it is called.


1. The length of the values array is called the "arity". Strictly speaking, to add to the same list, the name and "arity" must be the same. If the length of the values array is different, it is considered a different list in the same way that functions in C# are different if they have different numbers of arguments.

2. YP.assertFact actually stores the values in an IndexedAnswers object which is indexed dynamically to be very efficient.

3. Prolog uses the term "predicate", but we say "function" because we are focussed on the function which the compiler produces for the target language.

4. In the future, Yield Prolog may support general module names but this is more complicated because we would need a system where you register a class with a module name. For now, we just allow '' which means "the same class as the caller" so the caller can just use the locally defined getDeclaringClass to find its own "module".

If you really need to statically call a function in a different class (until Yield Prolog supports general module names), there is an inelegant way to do it. Here is how to call parent if it is defined in OtherClass:
:- import('', ['OtherClass.parent'/2]).
uncle(Person, Uncle) :-
'OtherClass.parent'(Person, Parent),
brother(Parent, Uncle).
In other words, you substitute parent everywhere with 'OtherClass.parent'.