Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The absence of being #648

Closed
pjohnston-wiley opened this issue Apr 28, 2018 · 2 comments
Closed

The absence of being #648

pjohnston-wiley opened this issue Apr 28, 2018 · 2 comments
Labels
defer Issue deferred to future Working Group framing spec-design syntax

Comments

@pjohnston-wiley
Copy link
Contributor

Background

In the constraint language SHACL, you can express a constraint that says a given predicate is not present. For example, if i want to say that there must not be any instances of ex:knows i can say (in Turtle form):

<x> a sh:PropertyNode ;
  sh:path ex:knows ;
  sh:hasValue () .

Here, () is the empty list, which according the Turtle spec, is equivalent to writing out in full:

<x> <http://www.w3.org/ns/shacl#hasValue> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .

I have been trying to find an elegant way to represent SHACL constraint graphs in JSON-LD through framing, and have been running into some issues.

The problem

Consider the following expanded form:

{
  "@id": "http://example.com/RubbleConstraint",
    "@type": "http://www.w3.org/ns/shacl#PropertyShape",
    "targetClass": "http://example.com/Rubble",
    "http://www.w3.org/ns/shacl#hasValue": [
      {
      "@list": []
      }, 
      {
        "@id": "http://example.com/Fred"
      },
      {
        "@id": "http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"
      }
    ]
}

The example is artificial: normally, sh:hasValue would only have one of these. However, each is a legitimate use case on its own. The first and the last are different ways of expressing the same thing, that there should no value, and the second is the normal use case of imposing a specific value on the constraint.

If i expand this on the playground, i get:

<http://example.com/RubbleConstraint> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/shacl#PropertyShape> .
<http://example.com/RubbleConstraint> <http://www.w3.org/ns/shacl#hasValue> <http://example.com/Fred> .
<http://example.com/RubbleConstraint> <http://www.w3.org/ns/shacl#hasValue> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
<http://example.com/RubbleConstraint> <http://www.w3.org/ns/shacl#hasValue> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .

which is semantically correct, if redundant, and really should just be:

<http://example.com/RubbleConstraint> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/shacl#PropertyShape> .
<http://example.com/RubbleConstraint> <http://www.w3.org/ns/shacl#hasValue> <http://example.com/Fred> .
<http://example.com/RubbleConstraint> <http://www.w3.org/ns/shacl#hasValue> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .

Expansion misses the semantics

If i expand, i get:

[
  {
    "@id": "http://example.com/RubbleConstraint",
    "@type": [
      "http://www.w3.org/ns/shacl#PropertyShape"
    ],
    "http://www.w3.org/ns/shacl#hasValue": [
      {
        "@list": []
      },
      {
        "@id": "http://example.com/Fred"
      },
      {
        "@id": "http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"
      }
    ]
  }
]

which is a problem: in the world of JSON i expect things that are semantically identical to be expressed the same way. I suspect here the expansion algorithm is missing a trick.

Compaction is OK as long as expansion does its job consistently

Nevertheless, i can compact this:

{
  "@context": {
    "@vocab": "http://www.w3.org/ns/shacl#",
    "ex": "http://example.com/",
    "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "nil": {
      "@id": "rdf:nil",
      "@type": "@id"
    },
    "Fred": {
      "@id": "ex:Fred",
      "@type": "@id"
    },
    "hasValue": {
      "@id": "http://www.w3.org/ns/shacl#hasValue",
      "@type": "@id"
    }
  },
  "@id": "ex:RubbleConstraint",
  "@type": "PropertyShape",
  "hasValue": [
    {
      "@list": []
    },
    "ex:Fred",
    "rdf:nil"
  ]
}

which is OK, but i am left with this weird bit of LD in the JSON with @list. I can alias it to something, but if i am going to hand it off to someone else, i would want something prettier. The best i could muster is this:

{
  "@context": {
    "@vocab": "http://www.w3.org/ns/shacl#",
    "ex": "http://example.com/",
    "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "nil": {
      "@id": "rdf:nil",
      "@type": "@id"
    },
    "Fred": {
      "@id": "ex:Fred",
      "@type": "@id"
    },
    "hasValue": {
      "@id": "http://www.w3.org/ns/shacl#hasValue",
      "@type": "@id"
    },
    "hasEmptyValue": {
      "@id": "http://www.w3.org/ns/shacl#hasValue",
      "@type": "@id",
      "@container": "@list"
    }
  },
  "@id": "ex:RubbleConstraint",
  "@type": "PropertyShape",
  "hasEmptyValue": [],
  "hasValue": [
    "ex:Fred",
    "rdf:nil"
  ]
}

which seems to be the best you can do and still be reversible.

And now framing

With framing, however, reversibility is not the goal, and data loss can be actively sought. Ideally i would want either:

{
  "@id": "ex:RubbleConstraint",
  "@type": "PropertyShape",
  "hasValue": [
    [],
    "ex:Fred"
  ]
}

or

{
  "@id": "ex:RubbleConstraint",
  "@type": "PropertyShape",
  "hasValue": [
    "nil",
    "ex:Fred"
  ]
}

However, because of the dual nature of objects of sh:hasValue, and because JSON-LD doesn't understand the special nature of the empty RDF list, there is no way to do this (other than alias bifurcation as i did in compaction). For example, the frame:

{
  "@context": {
    "@vocab": "http://www.w3.org/ns/shacl#",
    "ex": "http://example.com/",
    "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "nil": {
      "@id": "rdf:nil",
      "@type": "@id"
    },
    "Fred": {
      "@id": "ex:Fred",
      "@type": "@id"
    }
  },
  "hasValue": {}
}

gives you this:

{
  "@context": {
    "@vocab": "http://www.w3.org/ns/shacl#",
    "ex": "http://example.com/",
    "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "nil": {
      "@id": "rdf:nil",
      "@type": "@id"
    },
    "Fred": {
      "@id": "ex:Fred",
      "@type": "@id"
    }
  },
  "@graph": [
    {
      "@id": "ex:RubbleConstraint",
      "@type": "PropertyShape",
      "hasValue": [
        {
          "@list": []
        },
        {
          "@id": "ex:Fred"
        },
        {
          "@id": "rdf:nil"
        }
      ]
    }
  ]
}

The interesting thing here is that framing does not resolve {"@id": "ex:Fred"} back to Fred. I am not quite sure why.

A standard model of emptiness

In summary, it would be useful if JSON-LD recognized the quantum duality of the empty list, in that it can be both value (rdf:nil) and list ([]). I would propose:

  • Expansion standardizes what it does with empty lists whatever the form of the input. For consistency, rdf:nil might be best.
  • Framing adapts to the needs of the frame. My current half-baked thought would be if i say "hasValue": {} in my frame, and the input is "hasValue": "rdf:nil", output would be a contextualized rdf:nil, unless "hasValue": {"@container": "@list"}, in which case output would be []. I realize this likely brings up other issues.

Finally, it would be good if the working group, once established, were to re-examine the definition of @null in the spec, or at least clarifies its relationship to RDF, and rdf:nil in particular. Currently, rdf:nil is not the same as @null, and framing treats @null as null in the output JSON, which is swallowed by at least Java deserializers, so just takes up space, and emptiness should never take up space. I touched on the this in issue 641: it would be better if a property were only set to @null if it actually meant something.

If you got this far, thank you for your patience,
Patrick Johnston

@azaroth42 azaroth42 added defer Issue deferred to future Working Group spec-design syntax framing labels May 7, 2018
@azaroth42
Copy link
Contributor

Agree an issue, defer to WG time frame.

@gkellogg
Copy link
Member

Closed in favor of w3c/json-ld-syntax#18.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
defer Issue deferred to future Working Group framing spec-design syntax
Projects
None yet
Development

No branches or pull requests

3 participants