The Fencore design

Designing a new programming language based on logic and the Semantic Web. If you want to get the big picture, start by reading the introduction, then read chronologically.

Monday, October 17, 2005

Triples

Do you speak Prolog? When I first learned about it, what felt like the main idea was that every "function" was bidirectional. In another language you would write append(list1, list2), which would return the concatenation of list1 and list2. In Prolog you would write append(list1, list2, result), which would be a three-argument predicate about lists; it would be true if result was the concatenation of list1 and list2.

You could then ask, given list1 and list2, what is result? But you could also ask, given list1 and result, what is list2? Or, given just result, what are the possible combinations of list1 and list2 whose concatenation is result?

So in Prolog, you define a "function" in much the same way as you would in a functional programming language, but you get the inverse of that function for free. I find that cool :-)

Well, now take the framework of unary functions underlying the lambda calculus, which I talked about earlier. Here, every function has only a single parameter, so it's fn(arg). If you do the Prolog thing to that function, you get fn(arg, result), a binary predicate -- a world where everything is expressed in terms of binary predicates, does that sound familiar? :-)

Note that functions can be interpreted as Prolog predicates, but predicates cannot necessarily be interpreted as functions. Given a binary predicate p and an argument arg, p(arg, result) may be true for more than one result. However, p can be interpreted as a nondeterministic function, i.e. a function that returns zero, one, or any number of results. So one way we can look at RDF predicates is as nondeterministic unary functions.

It caused me some trouble to figure out how to map function/argument/result to the subject/predicate/object of RDF. It's clear that the function must be the predicate, but is the argument the subject or the object?

At first, I thought that the argument should be the subject, and the result should be the object. For example, the triple
    15 succ 16.
would say that (succ 15) = 16, i.e., that 16 is the successor of 15. For one thing, N3 uses this convention. For another, sin(0) = 1 seemed more like a statement about zero ("the sine of zero is one") than a statement about one ("one is the sine of zero"), so it seemed appropriate to have zero be the subject of the triple. Finally, triples are conventionally written in the order "subject predicate object," and I liked the correspondence to "input program output."

The only thing that "result function argument" had going for it was that it preserved the order (funcion argument) that is standard in functional programming. I thought the previous points were stronger than this one, and thought that "argument function result" was the better translation.

But then I noticed that an important use for terms is to be the way to write values; for example, the natural number 2 could be written as (succ (succ zero)). So the translation of this term would be the way to say "_:two is the natural number two" in RDF. And the conventions of RDF suggest strongly that _:two should be the subject of such a statement; compare
    _:l rdf:first "foo".
_:l rdf:rest rdf:nil
It seems to me that the "natural" translation of (succ (succ zero)) to triples would, thus, be
    _:two x:succ _:one.
_:one x:succ x:zero.
And these triples are in the order (result function argument). Therefore, I'm choosing that order.

In functional programming, the list above would be represented as (cons "foo" nil), which is an abbreviation for ((cons "foo") nil); this would translate to RDF as,
    _:l _:f rdf:nil.
_:f x:cons "foo".
(Never mind now that the RDF spec does not allow blank nodes to be predicates; we'll just ignore that for the moment.) So in RDF, there are established conventions for multi-argument functions, which are different from those used in the lambda calculus, and which are more useful in the RDF setting, I think.

The two-argument function cons is translated to two functions rdf:first and rdf:next. These are non-deterministic functions, because there is more than one resource with a particular rdf:first and more than one resource with a particular rdf:rest. In a sense, we then take the intersection of the results of these two functions.

This translation doesn't work for every binary function. Consider plus. We have both
    (plus 1 3) = 4
(plus 2 2) = 4
so we certainly can't write
    _:four x:plusLeft _:one.
_:four x:plusRight _:three.
because then we'd also have
    _:four x:plusLeft _:two.
_:four x:plusRight _:two.
and therefore also
    _:four x:plusLeft _:one.
_:x:plusRight _:two.
which is not what we want.

The RDF community, however, already has a well-known convention to work around this, documented in "Defining N-ary Relations on the Semantic Web: Use With Individuals." Using this translation, we'd have,
    _:four x:plus _:rel1.
_:rel1 x:left _:one.
_:rel1 x:right _:three.

_:four x:plus _:rel2.
_:rel2 x:left _:two.
_:rel2 x:right _:two.
So this is what we use for n-ary functions when the "simple" translation doesn't work.

0 Comments:

Post a Comment

<< Home