A little "what's that and why should I care".
Semantic JSON is about tagging JSON keys and strings with a semantic tag. It can be seen as a typed version of JSON, that still respects the original syntax of JSON.
For example, in typical JSON, you could have:
workspace.foo = {
"ownership": {
"owner": "Janis",
"owned": "Mercedes"
}
}
Sorry Janis, I love you
But after all, strictly speaking, "ownership" is just a Javascript object, and "Janis" and "Mercedes" are just Javascript strings.
The developer of a library, or the community of users of a library, can choose to follow a convention like:
in an "ownership", "owner" is always a human and "owned" is always an object. But the truth is that "Mercedes" is a string that could represent a brand as well as the name of a tap dancer.
Now if we use Semantic JSON:
workspace.foo = {
"<relation>ownership": {
"<slot>owner": "<person>Janis",
"<slot>owned": "<vehicle>Mercedes"
}
};
We make it clear that "ownership" is a relation, that "owner" and "owned" are relation slots, that "Janis" is a person and that "Mercedes" is a vehicle. I say it again: "Mercedes" is a vehicle, not a brand. We could have been talking about the "Mercedes" brand, but we're not. Here we're talking about a vehicle.
What that means is that Semantic JSON can directly represent real life objects, events, concepts, ...etc. Which is a huge step forward, IMO.
Ok, nothing spectacular. We have RDF and OWL for instance, so Semantic JSON doesn't bring any magical trick on the table. But it is convenient, lightweight, easy to use. It will integrate nicely with JSON Schema (that's the next step), and there should be a bridge to and from RDF and OWL later.
The idea came from another thread, where I set up a syntax for relations, semi-structured data, and behaviors. You remember this:
namespace prefix : "filename"
item name -> [ meta | data ]
item name -> relation type { slot1: item1, item2, ... ; slot2: item3; }
item name -> ( behavior node, ... , ... )
It's all good, the syntax is clean, and it's easy to parse with PEGjs for instance. But I wanted to be able to express these directly in JSON, because I wanted it to be well integrated in Javascript.
Ultimately, the aim is to implement the huge and ever growing list started in the "States / Processes / EventActs" thread.
The "why" of the proxy thing came from the same other thread, when I tried to make a tiny conscious program. You remember this:
function log(x) { console.log(JSON.stringify(x)); }
var entity = {};
entity.externalSensors = [0, 0, 0, 0, 0, 0, 0, 0];
entity.externalActuators = [0, 0, 0, 0];
entity.internalSensors = [0, 0, 0, 0];
entity.instantMemory = [
[0, 4, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
entity.target = [18, 1, 2, 3];
entity.behavior = {
3: [0, 2, 3, 0],
5: [0, 1, 0, 0],
8: [1, 0, 2, 0]
}
entity.setActuator = function(actuatorId, newValue) {
entity.externalActuators[actuatorId] = newValue;
entity.internalSensors = [0, actuatorId, newValue, 0];
}
entity.refreshInstantMemory = function() {
entity.instantMemory.push(
entity.externalSensors
.concat(entity.internalSensors)
.concat(entity.externalActuators)
);
entity.instantMemory.shift();
}
entity.logInstantMemory = function() {
for (var i=0; i<entity.instantMemory.length; i++)
console.log(JSON.stringify(entity.instantMemory[i]));
console.log();
}
entity.setTarget = function(targetSlot, newAddress) {
entity.target[targetSlot] = newAddress;
entity.internalSensors = [1, targetSlot, newAddress, 0];
}
entity.xy = function(n) { return { x: n%16, y: Math.floor(n/16) }; }
entity.fetch = function() {
var values = [];
for (var t=0; t<4; t++) {
var addr = entity.xy(entity.target[t]);
values.push(entity.instantMemory[addr.y][addr.x]);
}
return values
}
entity.interpret = function(values) {
var candidate = 0;
for (var i1=0; i1<3; i1++) {
for (var i2=i1+1; i2<4; i2++) {
if ((values[i1]==values[i2]) && (values[i1]>candidate))
candidate = values[i1];
}
}
return candidate*2;
}
entity.do = function(action) {
if (action[0]==0) setActuator(action[1], action[2]);
if (action[0]==1) setTarget(action[1], action[2]);
}
entity.run = function() {
while (1) {
entity.refreshInstantMemory();
entity.do(
entity.behavior[
entity.interpret(
entity.fetch())]);
}
}
entity.run();
This brings the idea of "internal sensors" by which a program can inspect its behavior as if it were observing something external (like another program). I think this capability (self observing) is essential if we're gonna make a conscious program.
Semantic JSON as it is today allows setting hooks on the usage of semantic tags. Once the hook is set, we use the "observable" objects as usual, no special syntax or weird thing: everything is automatic.
There's only one thing. The
tag tag is forbidden, because it is used internally to store the semantic tag associated with a key.
If you look closely, you'll notice that a semantic tag is not associated with the described object. Instead, it is associated with
the key which holds the reference to that object. This is by design.
An object X can be referenced several times by different keys, in different objects. The semantic tags represent
views of the object X, not the object itself. Think about it: Janis can be the mother of someone, and the sister of someone else. So, "mother" and "sister" are not associated with Janis, they are views which are associated with the
references to Janis in her child and her brother.
Function calls are observable too. The "sense" hook is called twice: once before the call, and once after the call. The hook handler is given a
CallId which uniquely identifies the function call, so we can know which "before call" corresponds to an "after call".
When using observed functions, a dev should always use
atomic parameters as arguments. In other words, use only variables, or constants, or function calls.
// good call, we have 1 variable and 1 constant:
workspace.fun.mul(workspace.foo.x, 3);
// bad call, we have a sum and a constant:
workspace.fun.mul(workspace.foo.x + workspace.foo.y, 3);
If we use a sum as argument, the observer won't be able to understand where the argument came from.
Also, make observable functions for everything, including the simplest things like sums. If you don't, things won't be observed. Soon I'll try to make clean loop observable functions, and also conditional observable function. It will probably end up looking like RoyalScript.
The point of observing functions would be, ultimately, to make the program understand how it works internally, and modifying itself
on purpose.
One thing I'll probably do soon is to log events in a memory structure. The interface should allow choosing whether or not we want to memorize events associated with tag X, how many events we want to memorize, for how much time we want to memorize, ...etc.
Next, the roadmap is like: link it to Javascript Events API, then integrate with JSON Schema, then bridge it to/from RDF and OWL with translation functions.