Plang uses a C-style syntax for statements, and a Prolog-style syntax for expressions (usually called "terms"). The simplest application is the traditional "Hello World". We will use the stdout module to print strings:
:- import(stdout). main(Args) { stdout::writeln("Hello World!"); }
We can run this with the plang
front-end to get the expected result ($
indicates a command that is typed at the operating system's command-line):
$ plang hello1.lp Hello World!
The ".lp" file extension is recommended for Plang source files. It stands for "Logic Program".
See the manual page for the plang
front-end for more information on running applications with it.
A more complex example uses an if statement to perform different actions depending upon an argument:
:- import(stdout). main(Args) { if ("--bye" in Args) stdout::writeln("Bye World!"); else stdout::writeln("Hello World!"); }
Running our second version, we get the following:
$ plang hello2.lp Hello World! $ plang hello2.lp --bye Bye World!
As can be seen, the command-line arguments are passed to the Plang application. Let's dump them to see what we get:
:- import(stdout). main(Args) { stdout::writeln("Args: " + Args); if ("--bye" in Args) stdout::writeln("Bye World!"); else stdout::writeln("Hello World!"); }
$ plang hello3.lp --bye Args: ["hello3.lp", "--bye"] Bye World!
The real power of a logic programming language comes with back-tracking, or the ability to search for multiple solutions amongst many alternatives. Let's start by establishing some simple facts about the people that exist in our world:
:- import(stdout). :- import(findall). person("Frank"). person("Mary"). person("George"). person("Amy").
We can dump a list of all people by finding all solutions to the query "Is P a person?":
main(Args)
{
findall(P, person(P), People);
stdout::writeln("People: " + People);
}
$ plang hello4.lp People: ["Frank", "Mary", "George", "Amy"]
The findall/3 predicate finds all solutions to the query in the order in which they are listed in the fact database. Let's now add some more rules for the relationship "A is a parent of B":
parent("Frank", "Mary"). parent("Frank", "George"). parent("Mary", "Amy").
We can then ask for a list of all Frank's children as follows:
main(Args) { findall(C, parent("Frank", C), Children); stdout::writeln("Frank's children: " + Children); }
$ plang hello5.lp
Frank's children: ["Mary", "George"]
So far all we have done is assert basic facts into the database. We now want to do some inferencing and ask "Is A a descendent of B?". This time we define a predicate that represents the rule:
descendent(C, P) { parent(P, C); } descendent(GC, P) { parent(P, C); descendent(GC, C); }
The first clause indicates that we can satisfy the descendent
relation if the second person is a parent of the first. The second clause indicates that the relation is also satisfied if we can find an intermediate child of the second person that the first person is a descendent of. In Prolog we would have written this as:
descendent(C, P) :- parent(P, C). descendent(GC, P) :- parent(P, C), descendent(GC, C).
In Plang, as in Prolog, variables are identifiers that start with an upper case letter. The engine will search for any variable binding that satisfies the rules. Any binding that does not satisfy the rules is discarded and another rule is tried.
To complete our example, we query for Frank's descendents:
main(Args) { findall(D, descendent(D, "Frank"), Descendents); stdout::writeln("Frank's descendents: " + Descendents); }
$ plang hello6.lp
Frank's descendents: ["Mary", "George", "Amy"]
The full source code for the example to date is:
:- import(stdout). :- import(findall). person("Frank"). person("Mary"). person("George"). person("Amy"). parent("Frank", "Mary"). parent("Frank", "George"). parent("Mary", "Amy"). descendent(C, P) { parent(P, C); } descendent(GC, P) { parent(P, C); descendent(GC, C); } main(Args) { findall(P, person(P), People); stdout::writeln("People: " + People); findall(C, parent("Frank", C), Children); stdout::writeln("Frank's children: " + Children); findall(D, descendent(D, "Frank"), Descendents); stdout::writeln("Frank's descendents: " + Descendents); }
$ plang hello7.lp People: ["Frank", "Mary", "George", "Amy"] Frank's children: ["Mary", "George"] Frank's descendents: ["Mary", "George", "Amy"]
We can use the plang
front-end in manual mode to directly query the database. First we will load our "hello7" example into the shell:
Plang version 0.0.1 Copyright (c) 2011 Southern Storm Software, Pty Ltd. Type 'help.' for help | ?- consult("hello7.lp"). yes
All of the predicates that we defined previously are now available. However, the main
predicate is not run automatically. Let's query for members of the parent
relationship:
| ?- parent(A, B). A = "Frank" B = "Mary" ?
The shell has returned the first solution that it found. The "?" prompt is asking us if we want to find another solution or stop. If we type ';', it will find another solution:
| ?- parent(A, B). A = "Frank" B = "Mary" ? ; A = "Frank" B = "George" ?
We can continue typing ';' until the shell says "no" (meaning that there are no more solutions), or hit Enter to abort the search. Let's ask a question that the database doesn't know the answer to:
| ?- descendent(D, "Amy").
no
We can even run the main
predicate directly:
| ?- main(_). People: ["Frank", "Mary", "George", "Amy"] Frank's children: ["Mary", "George"] Frank's descendents: ["Mary", "George", "Amy"] yes
The "_" variable is special - it indicates an anonymous variable with no specific name or value. Since our main
predicate does not process arguments, "_" is good enough for our purposes.
Finally, we exit the shell:
| ?- exit.