XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

RDF Applications with Prolog
by Bijan Parsia | Pages: 1, 2, 3, 4, 5, 6

DCGs Compared to XSLT

To use the transforming grammar requires a bit more infrastructure. In this case, I wrote a predicate, rss_to_html_list/3, which loads up an RSS 1.0 file, invokes the grammar to generate the HTML, then writes it all to a specified file. I could call it from a GUI front end or just package it up in a shell or CGI script. I backtranslated the grammar into an XSLT stylesheet. Comparing the two is instructive.

Prolog with DCG Comparison with XSLT
:- use_module(library(rdf_db)). 
:- use_module(library(html_write)).

rss_to_html_list(Resource, Source, Target) :-
Boilerplate setup, akin to

"<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0"...>"

  rdf_register_ns(rss, 'http://purl.org/rss/1.0/'),
  rdf_register_ns(dc, 
               'http://purl.org/dc/elements/1.1/'),
Namespace declarations, although two less than are necessary for the XSLT. Equivalent to the following attributes in the xsl:stylesheet:

"xmlns:rss="http://purl.org/rss/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/">"

  rdf_load(Source),        
  rdf(Resource, rss:title, literal(Title)),        
        
The first line corresponds to a command line option designating the source document. rdf_load/1 parses the file and creates an in-memory (in this implementation) representation of the RDF statments that may be queried by means of appropriate predicates (like rdf/3). An XSLT processor would take the designated file and parse it into an in-memory representation (typically) -- often a DOM tree -- which may be queried by means of appropriate XPath expressions.

The second line is a query which grabs a value and binds the variable Title to it. This is roughly equivalent to

<xsl:variable name="title"
select="rdf:RDF/rss:channel/rss:title"/>

One point of interest: Binding and referencing variables in XSLT is fairly painful. It's not just that it's nastily verbose (okay, it's mainly that it's nastily verbose), but that there are lots of different rules and contexts for getting back the value. Aside from that, XSLT has a fairly straightforward "assignment" model of binding (explicit) variables. The Prolog variable is (1) structural (i.e., the variable is just a "hole" in the larger term, and (2) the variable is "two-way" (i.e., you can pass a value to the query by that variable or get one out through it).

  phrase(rss_html_list(Title), TargetHtml),
  tell(Target),
First line causes the DCG to be evaluated and the result bound to TargetHtml. Second line opens the result file for writing (both of these probably fall in the scope of command line option or calling function stuff for XSLT).
  print_html(TargetHtml),        
  told.
<xsl:output method="html"/> 

print_html/1 is an html_write convenience predicate which takes the result tree from the DCG grammar and serializes it in standard HTML format. This is what to change if, for example, you'd want XHTML output.

rss_html_list(ChannelTitle) --> 
    page(\rss_list_head(ChannelTitle), 
         \rss_list_body(ChannelTitle)).
<xsl:template match="/">
   <html>   
       ...
   </html>
</xsl:template>
rss_list_head(PageTitle) -->
     html([title([PageTitle])]).
        
rss_list_body(Header) --> 
    {setof(Item, 
           rdf(Item, rdf:type, rss:item), 
           Items)},

    html([h2(align(center),[Header]), 
          ul(\rss_list_items(Items))]).
<head>
 <title>
  <xsl:value-of select="$title"/>
 </title>
</head>

<body>
     <h2 align="center">
       <xsl:value-of select="$title"/>
     </h2>
     <ul>
       <xsl:apply-templates
           select="rdf:RDF/rss:item"/>
     </ul>     
</body>

rss_list_items([First_item|Rest_of_items]) -->
    html([li([\list_item_content(First_item)])]),
    rss_list_items(Rest_of_items).
rss_list_items([]) --> [].
<xsl:template match="rdf:RDF/rss:item">  
   <li>...</li>  
</xsl:template>
list_item_content(Item) --> 
    {rdf(Item,dc:description,literal(Description))}, 
    html([\rss_link(Item),br([]),Description]).
...
<br/>
<xsl:value-of select="./dc:description"/>
rss_link(Item) --> 
    {rdf(Item,rss:link,literal(Link)),  
    rdf(Item,rss:title,literal(Title))}, 
    html(i(a(href(Link),Title))).
<i>
   <a href="{./rss:link}">
      <xsl:value-of select="./rss:title"/>
   </a>
</i>

Standing back and looking at the XSLT sheet right after writing it, it seemed much clearer than the DCGs. A large part of this is that I'm more accustomed to writing HTML and HTML templates with embedded code than writing DCG templates. But I'm finding that the DCGs are more helpful for thinking through the problem.

Conclusion

Since the inferences in this transformation were trivial, it doesn't involve any particularly sophisticated Prolog, and yet this is precisely the kind of everyday task many of us find ourselves doing all the time. The Semantic Web, if it's to work out, will be made up as much of the ordinary and familiar as of the exotic.

Useful Links

The Transformation Scripts

SWI-Prolog

SWI-Prolog Based RDF Applications