Designing the Kynetx Rule Language


The graphic used to indicate Information Card ...

Image via Wikipedia

KRL, the Kynetx Rule Language, is a domain specific language that operates on the Kynetx Network Service (KNS). When I first started building KNS the first priority was getting a rude dog demo working that we could use to start having customer and investor conversations. Consequently, I was less than pure in some of the implementation details.

For example, the conditionals in if statements were just single predicates and the declarations in the rule prolog were all one-offs. Later I went back and wrote a complete, generalized expression language that could be used in the conditionals and in the actions, but I'd never gone back to redo how declarations were done in the prolog.

A need to query user-defined data sources and then do something with the results, led me to determine that using generalized pattern-based selectors were the most general and powerful means of interacting with the data (think XPath, CSS selectors in jQuery, and JSONPath).

Unfortunately, when I dug into the code, I remembered I'd incurred a little programming debt and needed to clean up the declarations in the prolog rather than add yet another piece of one-off code to that part of the interpreter. Not only did this mean I only needed to deal with the selector expressions once, but it also greatly simplified the declaration code while making declarations in the language much more powerful.

To see this in action, imagine the following scenario. You've got a queriable data source at a URL:

http://www.example.com/bookdata?isbn=xxx

that returns JSON data telling you if a book is available at your local library. The JSON might look like this:

{"responseHeader":{"status":0,
                   "QTime":1,
                   "params":{"q":"0316160202","wt":"json"}},
 "response":{
     "numFound":1,
     "start":0,
     "docs":[
          {"isbn":"0316160202",
           "title":"Eclipse",
           "url":"http://library.com/search/?isbn=0316160202"
           } ] } }

You could create a KRL ruleset that queries that data set and puts a Growl-like notification at Amazon informing you of that fact for any book page you visit like so:


ruleset library {

    global {
\tdatasource library_search 
                  <- "http://www.example.com/bookdata/?wt=json";
    }

    rule book_notfication is active {
        select using "www.amazon.com/gp/product/(\\d+)/" 
                 setting(isbn)
        pre {
      \t  book_data = datasource:library_search("q="+isbn);
  \t  url = book_data.pick("$..docs[0].url");
 \t  title = book_data.pick("$..docs[0].title");

\t  msg = <<
This book's available at your local library. 
Click here to see: <a href="#{url}">#{title}</a>
>>;
\t}
\tif(book_data.pick("$..numFound") > 0) then {
\t\tnotify("top-right", "#222", "#FFF", 
\t\t       "Local Library", true, msg);
\t}
    }
}

If you're interested in the details of how this works, there's some KRl documentation available online and more to come soon. The pick operation is applying a JSONPath pattern to select specific parts of the JSON data. I'm happy to explain any of it in more detail, so just ask.

As I describe here, you'd enable this functionality by installing a small Information Card (we call them KIX) in your card selector. The rule lives and executes in the cloud.

I love when there's something new someone wants to do that we can't quite do yet. I get a chance to build more on the language. There is a grand design, but I can only afford to build out the pieces we need right now and even at that, I can't really keep up. The key, I've found, is making sure I take time to refactor as I go. Otherwise, you get to the point where every task is too hard to do.