/subClassOf

Playing with Prototypal Stuff

Primary LanguageJavaScript

subClassOf

##Live at mathbiol.github.io/subClassOf. To add it to your app all you need is

<script src="https://mathbiol.github.io/subClassOf/subClassOf.js"></script>

Synopsis

This is an attempt to come up with an implementation for Stefan Decker's ideas of prototypal inheritance at web scale. The key document is Stefan's presentation at CSHALS 2013. Slide #34 in particular will be repeatedly used here as an illustration for the proposed implementation. Note that the approach explored here pollutes the Object prototype (you were warned :-) ) with a subClassOf method to establish the proposed "horizontal dependency". This approach is inspired by type migration as in Semantic Web's rdfs:subClassOf.

slide 34 Figure: Slide #34 of presentation by Stefan Decker at CSHALS 2013

Stefan's car

Let's start by putting Stefan's car somewhere, say in the gh-page publicly served folder of this repository

https://mathbiol.github.io/subClassOf/StefansCar.json

As one can confirm by clicking on it, this URI is dereferenced to

{
  maker:"Volkswagen",
  color:"yellow",
  model:"Passat",
  ocm:2000
}

Therefore, we can define stefansCar by reference, as is customary in using the Web as a global data space:

stefansCar = "https://mathbiol.github.io/subClassOf/StefansCar.json"

So we should now be able to define hongGeesCar, which we hear is blue, by having all other types migrate from Stefan's car:

hongGeesCar = {color:"blue"}  
hongGeesCar.subClassOf(stefansCar)

This will take a second or two to dereference the remote prototype and import the new attributes, reporting that HongGee's car, while remaining blue, is also:

{
  maker:"Volkswagen",
  color:"blue",
  model:"Passat",
  ocm:2000
}

Thanks to JavaScript's functional support for lexical scoping, this first approach is fairly straightforward, as the inspection of the code will reveal. This initial, one off, support for prototypal inheritance will now be expanded to explore the local scope of the horizontal inheritance.

Callback

As it typical of web computing reliance on remote calling, the dereferencing of the remote prototype, Stefan's car, introduces an asynchrony which we could, for example, address with the customary callback function. Accordingly, .subClassOf will accept a callback function as a second input argument. For example, you could get the stringified original and type inherited result shown in the console by

hongGeesCar.subClassOf(
  stefansCar,
  function(x){console.log(JSON.stringify(x))} // callback function
)
JSON.stringify(hongGeesCar) // it will become available before the callback had a chance to come back

and the console would show

		{"color":"blue"}
		{"color":"blue","maker":"Volkswagen","model":"Passat","ocm":2000}

Callback is at the interesting transition

The use of callback functions is pretty standard in asynchronous workflows, so it is easy to miss the opportunity to explore its scope. Unlike promises or .onload, the callback function is executed within a scope that includes pointers to both the original object and the prototype with the remote attributes. For example, one can write callback functions that keep the inheritance up-to-date such that changes in the remote attributes will be updated in the domain object. In order to enable these exploits, the callback function will behave a differently when used with two input arguments:

function ( <Domain Object> , <Range Object URL>) { . . . }

Enough about Stefan's car

Indeed, he is probably now driving something better than that ugly yellow car anyway! In any case, we need a really good reason for polluting JavaScript's Object prototype. The specific motivation for this intrusion is the handling of a myriad of Biomedical data assets flooding a great diversity of BigData resources such as those managed by NCBI and EBI. The challenge is particularly complicated when the assets are in ftp sites like NCBI's or CORS-less web folders like TCGA's, but even EBI's impressive RDF platform will produce URI's to data files in formats that "everyone understands" but still need to be parsed. In this context, a pervasive .subClassOf comes with the promise of handling the format identification and parsing as a reward for the low level intrusion of .subClassOf into every Object. Lets get ready to try this out with some population health and also with some genomics data to see how this could help with real world data.

subClassOf has two dozen lines of code already - time to stop :-)!

The subClassOf.js library was developed to enable the sort of horizontal inheritance proposed by Stefan Decker. Therefore, uses of that capability are best left out of the core library. Accordingly, the examples discussed in the next sections will use functions stored in the subClassOfDemo.js. If you have your browser pointed to mathbiol.github.io/subClassOf then you already have it loaded. Otherwise add this additional source to your HTML:

<script src="https://mathbiol.github.io/subClassOf/subClassOfDemo.js"></script>

Do No Eval (which is to say .noEval=true)

Let's go back to the special place that the execution of the callback function is. In order to allow this experiment, the .subClassOf method will NOT evaluate the prototypal inheritance and instead will allow for a callback function with the subject of object as the .domain of the input variable and the HTTP call to the remote prototype as the range. Taking lots of liberties mixing rdf syntax with JavaScript functional style, this could be thought as

callback=function(x){
      x.domain --(subClassOf)--> x.range
  ... some code specifying the nature of the "inheritance" ...
  ... no limits here, you can go far beyond type migration ...
}

Lets illustrate this experiment by going back to Stefan's car one last time. The important thing to remember is that this inheritance space is opened for additional business when the callback function has a .noEval=true.

noEvalFun = function(x){
  // do something to the domain variable every second, for 10 seconds
  var i = 0
  var t=setInterval(function(){
  x.domain.graffiti="noEvalFun was here at "+Date() // write .grafiti onto domain object
  console.log(JSON.stringify(x.domain)) // display uggly domain graffiti in the console
    i++;if(i==10){clearInterval(t)} // count up to ten graffiti writings and then stop
  },1000)
  // make pointers to domain variable and HTTP call range available in the console
  console.log({
    domain:x.domain,
    range:x.range
  })
}
noEvalFun.noEval=true // <-- flagging callback function for noEval

Btw, the noEvalFun is included in subClassOfDemo.js so there is no need to copy it to the console, it is there already. Lets give it a go:

Let's start by recalling Hong Gee's car simply as something blue,

HongGeesCar={color:"blue"}

And then let's see what happens when noEvalFun is passed as the callback function

noEvalDemo

As illustrated by this example, the single input argument of the callback function:

  1. Has to have a .noEval=true to trigger the advanced behavior.
  2. Is automatically added a .domain attribute with a pointer to its subject, in this case Hong Gee's car.
  3. Is automatically added a .range attribute with a pointer to the remote object, in this case a HTTP call to the JSON object describing Stefan's car.
  4. Most importantly, the callback function benefits from the wonders of lexical scoping (javascript is a functional language). As illustrated by the graffitti attribute, these are lasting pointers, which can be used indefinitely by the callback function.

See next section for an example.

Self-updated inheritance

The default behavior of .subClassOf will add to the domain object only the new attributes found in the range object. What if we want the value of those new attributes to be kept updated in case they change in the remote range object? Here we'll use the .noEval behavior to encode an alternative to the default behavior. The selfUpdatedInheritance function used below is included in the subClassOfDemo.js script. The example below declares this horizontal dependency with a single line encoding for the attribute inheritance.

x.domain[a]=r[a]

We’ll now play with this declaration by calling it twice - the first time to take note of the new attributes and the second time to force the update of the values in the remote range onto the local domain object:

selfUpdatedInheritance=function(x){
    // find out what is new in the remote object
    var r = eval('('+x.range.responseText+')')
    var newAttr = {} // object with new attribute names

    // update new attributes only
    Object.getOwnPropertyNames(r).map(function(a){
        if(!x.domain[a]){
            newAttr[a]=true // noted for updating by newFun
            x.domain[a]=r[a] // up to here we're just mimicking the default behaviour
        }
    })
    var url = x.range.responseURL // link to where the remote prototype is
    
    // the new callback function that will be passed at regular intervals
    var newFun=function(x){ // This is the same as above 
                            // except that newAttr is borroed rather than determined anew
        var newr = eval('('+x.range.responseText+')')
        Object.getOwnPropertyNames(newr).map(function(a){
            if(newAttr[a]){x.domain[a]=newr[a]}
        })
        console.log(Date(),JSON.stringify(x.domain))
    }
    newFun.noEval=true

    // update new attribute values every 5 seconds
    var t = setInterval(function(){
        x.domain.subClassOf(url,newFun)
    },5000)
}
selfUpdatedInheritance.noEval=true

The asynchronous nature of this example lends itself better to a screencast rather than a snapshot. First the two steps, defining a local object, again Hong Gees's car as something blue and then we'll change values inherited horizontally from the remote Stefan's car:

HongGeesCar={color:"blue"}
stefansCar="https://mathbiol.github.io/subClassOf/StefansCar.json"
HongGeesCar.subClassOf(stefansCar,selfUpdatedInheritance)

and this is what happened:

It is important to note that the iterative implementation serves the purpose of emphasizing the asynchronous dependency. A more elegant implementation of the dependency would be achieved by relying on cloud services designed for real-time applications by supporting client-side listeners to server-side events, such as firebase.

Semantic Web - a global computational space?

At this point one might be excused to think that the reference to rdfs:suBClassOf is not only excessive but altogether abusive since there aren't any triples here to justify a role for the Resource Description Framework. The response to this anticipated criticism is a bit tentative, and reflects recent experimentation with pervasive web computing solutions such as QMachine, [PMID:24913605]. The experimental bit can be found in the way the callback function packages .domain and .range when the .noEval flag is used. For example, if one halts execution of noEvalFun above, the value of the input argument x will be found to be that of the noEvalFun function itself,

> x
subClassOfDemo.js:6 function (x){
    // do something to the domain variable every 2 seconds, for 10 seconds
    var i = 0
    var t=setInterval(function(){
            x.domain.graffiti="noEvalFun was here at "+Date()
            console.log(JSON.stringify(x.domain)) // display ugly domain graffiti in the console
            i++;if(i==10){clearInterval(t)} // count up to ten graffiti writings and then stop
        },1000)
    
    // make pointers to domain variable and HTTP call range in the console
    console.log({
        domain:x.domain,
        range:x.range
    })
}

> x.domain
Object {color: "blue", subClassOf: function}color: "blue"

> x.range
XMLHttpRequest {statusText: "OK", status: 200, responseURL: "http://localhost:8000/subClassOf/StefansCar.json", response: "{↵	maker:"Volkswagen",↵	color:"yellow",↵	model:"Passat",↵	ocm:2000↵}", responseType: ""…}

In the examples described here, the fact that the callback input argument packages the rdf:predicate function itself, in addition to its rdfs:domain and rdfs:range, is of no practical relevance. It is speculated however, that the dyadic predicate nature of this argument is conducive to web-scale code distribution and asynchronous execution along the lines explored by Sean Wilkinson's Q, which underlies QMachine. Specifically, the callback input argument contains the executable triple domain <- function - range, that is, a portable function(domain,range){...}. Could this use of .subClassOf be the foundation for "linked code" as a global compute space along the same lines that RDF did for linked data as a global data space?

Miscellaneous notes

  1. If the range is an actual object in the local scope instead of a URL, .subClassOf will work as one might expect - it will pull the new attribute values.

## Domain applications

Examples of applications to different domains

Population health

someDiseaseStats = {zip:11790,year:2013}.subClassOf(<openHealth SODAservice>)

as a more (semantically) interoperable version fo apps such as http://mathbiol.github.io/openHealth/index.html?jobs/pqiSuffolk.js

... work in progress ...

Genomics example

	somePatientCases = {path:"genes/ENSG00000141510",filters:{
		"mutation":{"functionalImpact":{is:["High"]}},
		"donor":{"projectId":{is:["MALY-DE"]}},
		"tumourStageAtDiagnosis":{"is":["III"]}}
	}.subclassOf("https://dcc.icgc.org:443/api/v1")

as a more (semantically) interoperable version for libraries such as https://github.com/ibl/icgc.

... work in progress ...