Introduction - rubyunworks/prime GitHub Wiki
Prime is actually a very simply language. If you have any understanding of Prolog, you are going get Prime almost immediately. If you don't understand Prolog, don't worry, it's not that hard --and just bare with us when we contrast Prime to Prolog.
Prime programs are written as a set of facts, each fact being a simple list of words. For instance lets write a very simple fact to which we all would agree.
Socrates is a man.
That was easy. Does it strike you that that is a perfectly acceptable Prime program? Okay, maybe not so useful yet, but lets add a bit more.
_ is mortal :- _ is a man.
Now this is a more interesting fact, because it doesn't just specify a static thing but relates two other facts together. It says that, if someone is a man, then they are also mortal. Guess what? We already told our program that Socrates is a man. So given our rule, it would not be unreasonable to expect it to infer the mortality of Socrates. Let's ask:
_ is mortal?
=> Socrates
Awesome. Good job computer! Notice we simply asked a question by changing the period to a question mark. Sound like any other language you know?
If you know Prolog, you know the above code is the same as the classic Prolog example:
mortal(X) :- man(X).
man(socrates).
?- mortal(P).
=> P = socrates
=> yes
The differences between Prime and Prolog here a faitly obvious. Prime's syntax is much less structured. It doesn't require all the parenthesis, commas, colons, etc.
Now let's take it up notch.
Tom likes Mary.
Tom likes trains.
Peter likes fast-cars.
person1' likes person2' :-
person1' has hobby hobby',
person2' has hobby hobby'.
John has hobby trainspotting.
Tim has hobby sailing.
Helen has hobby trainspotting.
Simon has hobby sailing.
Notice what we have done here. Instead of using the underscore _
, we have used named variable slots, these are similar to variables of other programming language. To designate a variable slot we simply append the name with an a prime mark, i.e. an apostrophe since our keyboards don't have a real prime mark. Using named variables we can make rules with much more complex relationships. The _
is generally reserved for slots that don't matter --they can be anything and what they are has no baring to what we are saying.
Now let's ask some questions.
John like trains?
=> true.
Yep, because it matches with the second fact in the database.
Helen likes John?
=> true.
Using the likes
rule we can see that Helen`s hobby is trainspotting and John's hobby is trainspotting, hence Helen likes John.
Time likes Helen?
=> false.
There are no simple facts to match this and when we try to use the rule we can not find a hobby which Tim shares with Helen, so it fails.
John likes Helen?
=> true.
Similar to proving that Helen likes John, we use the rule for likes and find that they both have the same hobby. This time, however person1'
is bound to John and person2'
is bound to Helen. This was the other way round when we proved that Helen likes John.
Now let's ask a really open-ended question. Who likes who?
_ likes _?
=> Tom likes Mary.
Tom likes trains,
Peter likes fast-cars,
John likes Helen,
Helen likes John,
Tim likes Simon,
Simon likes Tim.
Awesome.
Strings
Instead of words, called atoms in technical lingo, we can also use strings in our rules.
customer 1 is "John James".
Not only does this allow us to use spaces with our atoms, it also allows us to utilize character encodings, the default being UTF-8.
Lists
A list in Prime is represented much as they are in Prolog, e.g. [a, b, c]
. And Prime, like Prolog, can do head-tail pattern matching, but to do this it uses a slightly different notation. For example, in Prolog one might write:
in(H, [H|T]).
In Prime we would write:
h' is in [h',t*].
Clearly this fact states that if something is the head of the list, then it is in the list. WE can admire that the Prolog code is so concise, but it also proves more difficult to understand, especially as rules become more complex. The Prime code, while not quite as concise, reads almost like English. There is almost no barrier at all to comprehending it's meaning.
Ok, our simple fact is pretty trivial. But now watch this:
x' is in [_,t*] :-
x' is in t'.
Now we are saying that something is in a list if we can prove that, although it isn't the head of the list, it is nonetheless the head of rest of the list. This is called a recursive rule, by the way, because it refers to itself. In Prolog, if your were curious, that would be written:
in(X,[_,T]) :-
in(X,T).
Given this rule we can query to see that it actually works.
c is in [a,b,c]?
=> true.
Sure enough.
Maps
Now here is something that Prime has that Prolog does not, maps. Maps are like lists but instead of being filled with simple values they are filled with key-value pairs, like so:
[a:1, b:2, c:3].
We can treat maps in much the same way as we treat lists. The only difference is that we can make special queries of them based on their keys.
TODO
Trees and Graphs
Prime can work with trees and graphs just as easily as it can work with lists...
Tables and Matrices
TODO: Are lists of lists enough to represent tables and matrices?
Math
Prime has build math logic. But you might be a little suprise by how fits in the rest of the logic.
player' score is s' :-
player' hit h' bad guys,
player' level is l',
s' = l' * h'.
Subfacts
Sub-facts are facts within facts. Sub-facts allow us built more complex facts. For example:
a red candle.
a silver axe.
(a red candle) is located in the kitchen.
(a silver axe) is located in the garage.
We can also use sub-queries to write more concise rules.
player' score is s' :-
s' = (player' hit _ bad guys) * (player' level is _).