This is meant to be the first of a series of posts in which I will
develop the bussiness logic of a social networking application,
implemented as an nl
ontology. The application might be, for example,
a social web site, though the user interface, and its bindings to the
nl backend, (the views and the controllers, if we follow the MVC
paradigm) will not be developed here.
In this application the users will correspond to objects in the knowledge base.
They will be able to have different kinds of relationships among them,
and with other kinds of things, and this relations will
also be reflected in the knowledge base.
They will as well be able to perform actions, which may have effects on
their relationships, in ways determined by our ontology.
In this first part I will design the way the nl backend will interface
with the controllers. This is to say, the way the users’ input will be
translated into nl. In general, this will take three basic steps:
- In the first
step, the controller will translate the request from the user into
an nl sentence with the form “such user wants to do such thing now”,
and will enter this sentence into nl’s knowledge base.
- In the second step, the knowledge base will be
extended to all it’s logical consecuences.
- And in the third step, the kb will be queried for a sentence with
the form “such user does such thing now”. If it founds it there (i.e., if
this second sentence was entailed by the one entered in the first step),
the controller will trigger the action that the user requested.
So, lets code this. First we need to import a couple of classes from nl
>>> from nl import Thing, Exists, Fact
We now define two subclasses of Thing, whose instances will denote
users and content items in the sentences that we will later put into the
kb.
>>> class Person(Thing):
... """ A person is a thing """
>>> class Content(Thing):
... """ A content item is a thing """
The Content class may be further subclassed to provide for different
types of content: images, articles, etc. Let’s just add images as an example:
>>> class Image(Content):
... """ An image is a content item """
We now define a primitive verb Lives with the only constraint that sentences
built with it need to have a Person instance as subject. We will then extend
this verb to express any action or condition or relation that can affect people.
Later on, this primitive verb will be useful to make statements about all those
verbs as a whole.
>>> class Lives(Exists):
... """ People can do things and relate to other things """
... subject = Person
We might extend Lives to produce more specific verbs; for example, we might add
a verb to the effect that people can view content items:
>>> class Views(Lives):
... """ People can view content items """
... mods = {'what': Content}
With what we have so far, we might be able to build sentences
asserting that users do things. Let’s see an example of how would
this be accomplished, in which we have a
user John, a picture img1, and we say that John views img1:
>>> john = Person('john')
>>> img1 = Image('img1')
>>> f1 = Fact(john, Views(what=img1), 'now')
What we are doing with these instantiations is building sentences, that would
be understood as “john is a person”, “img1 is an image”, and “John views img1 now”.
Note that we are only building them, and not entering them into nl’s knowledge
base (we will be doing that further down).
Finally, we need some kind of permission system. We want to express that certain
people can (are allowed to) do certain things, and that people may want
things to happen. For this purpose we define two verbs, that take a Person
instance as subject and a Lives instance (i.e., a predicate) as a modifier:
>>> class Can(Lives):
... """ People can do things """
... mods = {'what': Lives}
>>> class Wants(Lives):
... """ People want things to happen """
... mods = {'to': Lives,
... 'who': Person}
With this, we may form sentences such as “John wants to view img1”, like This:
>>> f2 = Fact(john, Wants(who=john, to=Views(what=img1)), 'now')
With all this in place, we can add a rule to express that, if someone wants to
do something at a certain time, and can do it, he simply does it.
>>> from nl import Rule, Instant, Duration, During
>>> r1 = Rule([
... Fact(Person('P1'), Wants(who=Person('P1'), to=Lives('L1')), Instant('I1')),
... Fact(Person('P1'), Can(what=Lives('L1')), Duration('D1')),
... During('I1', 'D1'),
... ],[
... Fact(Person('P1'), Lives('L1'), Instant('I1'))])
As it is explained in nl’s documentation, an Instant denotes a point in time,
A Duration denotes an interval of time, and During('I1', 'D1') states a
condition that is True if the instant “I1” is within the bounds of the
interval of time “D1”.
Now, we can enter into the system a sentence such as the above f2, and, if there
is also a sentence (lets call it f3) asserting that he (John) can view img2, we will
have a sentence such as the above f1 entailed by the other two (f2 and f3). We can
imagine that f2 is entered into the knowledge base when the user
identified as “John” clicks on a link that points to img1, and that f3 is in the
knowledge base because the owner of img1 has wanted it to be there (as we shall
develop in a later post). Let’s try it:
>>> f3 = Fact(john, Can(what=Views(what=img1)), Duration(start='now'))
We have to enter these sentences into the knowledge base:
>>> from nl import kb
>>> kb.tell(r1, john, img1, f2, f3)
To get entailments we need to extend the knowledge base. Without extending it,
we only have what we have entered (note that we have not entered f1):
>>> kb.ask(Fact(john, Wants(who=john, to=Views(what=img1)), 'now'))
True
>>> kb.ask(Fact(john, Can(what=Views(what=img1)), Duration(start='now')))
True
>>> kb.ask(Fact(john, Views(what=img1), 'now'))
False
However, if we extend the knowledge base, we actually get that John views img1:
>>> kb.extend()
1
>>> kb.ask(Fact(john, Views(what=img1), 'now'))
True
Now, the reason that leads me to think that all this may be remarkable is that there
is only a first order theory behind
nl, but the “L1” logical variable in the “r1”
rule has the “form” of (though it is not) a second order variable. We can, in fact,
use variables that apparently have an arbitrarily high order, to say things like
“thinks that wants to view”. I say it has the form of a secon order variable because we might use is like this: Lives('L1')(what=Content('C5'))