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

Request for style guidance on top-level property shapes #172

Open
ajnelson-nist opened this issue Jan 12, 2023 · 1 comment
Open

Request for style guidance on top-level property shapes #172

ajnelson-nist opened this issue Jan 12, 2023 · 1 comment

Comments

@ajnelson-nist
Copy link
Contributor

Hello @ashleysommer,

In this comment, you offered some implementation-style guidance that makes me curious for a citable source. The purpose of this Issue is reconciling those recommendations against what I currently find to be benefits of going against the implementation style recommendations you noted.

... you will very rarely need to define a top-level shape to be a PropertyShape.
PropertyShapes are almost always a blank node that is the value of a sh:property constraint, and should never have targeting instructions baked in. Targeting methods like sh:targetSubjectsOf simply don't make sense on a property shape.

The SHACL specification does include examples of targeting methods used on named property shapes, e.g. from 4.2.1:

ex:MinCountExampleShape
	a sh:PropertyShape ;
	sh:targetNode ex:Alice, ex:Bob ;
	sh:path ex:name ;
	sh:minCount 1 .

and from 5.1:

ex:LanguageExamplePropertyShape
	a sh:PropertyShape ;
	sh:targetClass ex:Country ;
	sh:path ex:germanLabel ;
	# (snip)
	.

So, the specification is permissive of top-level, targeting sh:PropertyShapes, and my scan of the document for occurrences of the string "sh:PropertyShape" found no prescriptions against doing so. (I welcome a section reference if I missed such a prescription - completely possible.)

I have found some beneficial reasons for doing so. My community is developing an ontology as a mix of OWL and SHACL, and using SHACL for data enforcement rules. I have also been using SHACL to help understand other ontologies that are implemented solely in OWL.

To help verify that our usage of various rdf:Propertys is conformant with their definitions in our or upstream ontologies and RDF schemas, we have some shapes that are defined solely centered on when the property is used, agnostic to what class is using them.

Taking your example about the tally of tabby limbs:

ex:myShape
  rdf:type sh:PropertyShape ;
  sh:targetClass ex:Cat ;
  sh:path ex:numLeg;
  sh:maxInclusive 4;
  .

Thanks to wanting to share numLeg with other animal classes, I'd prefer to have ex:numLeg get some prescription about characteristics its usage will always require, and then leave it to classes to make their own inherent, blank-node sh:PropertyShapes to spell out their own requirements:

ex:Bird-shape
  a sh:NodeShape ;
  sh:targetClass ex:Bird ;
  sh:property [
    a sh:PropertyShape ;
    sh:maxInclusive 2 ;
    sh:path ex:numLeg ;
  ] ;
  .
ex:Cat-shape
  a sh:NodeShape ;
  sh:targetClass ex:Cat ;
  sh:property [
    a sh:PropertyShape ;
    sh:maxInclusive 4 ;
    sh:path ex:numLeg ;
  ] ;
  .

ex:numLeg-shape
  a sh:PropertyShape ;
  sh:targetSubjectsOf ex:numLeg ;
  sh:datatype xsd:integer ;
  sh:nodeKind sh:Literal ;
  sh:path ex:numLeg ;
  .

(I'll note that this implementation style is my preference, and other members of my community prefer the style that repeats all constraints due to at least some simplified documentation-scripting.)

The other reason I prefer making top-level sh:PropertyShapes is to give the shape an IRI so any SHACL violation reports are easier to reference to a specific shape instance in the ontology. Using the always-inlining style you'd suggested, all that gets reported in the sh:sourceShape is the blank node and its inlined constraints, and there is no hint about what the targeter was, so I would have to do a non-trivial review of the ontology (including SPARQL and mental class resolution) to find the sh:PropertyShape and its contextual sh:NodeShape. This problem is also met by following your suggested style of only ever using targeters on sh:NodeShapes.

So, for these reasons concerning constraint scoping and SHACL report usability, I see value in having sh:PropertyShapes that are independent of sh:NodeShapes. I'm interested in learning more about why you recommended what you did, especially if I'm settling on a practice that is going to be bite me spectacularly for some subtle reason.

@ashleysommer
Copy link
Collaborator

ashleysommer commented Jan 13, 2023

Hi @ajnelson-nist Thank you for your well thought out and well written post, you have presented some great arguments to consider.

Firstly, I want to state that I am not an authoritative or definitive source on SHACL style. My only experience with writing SHACL shapes and constraints is for the purposes of thoroughly testing PySHACL code paths, I have not used it in the real world personally, though I work with people who do.

My advice to you that - you've mentioned above - was drawn only from my experience writing the PySHACL validator and from helping users determine the cause of problems they are facing with PySHACL. All of my knowledge of how to write SHACL shapes and constraints comes from reading the tests and references in the W3C SHACL Test Suite (sht-tests), and the DataShapes org test SHACL test suite (DASH-tests). I admit these are probably not a great set of examples to learn from.

In the SHT-test suite core-tests, there are no examples of or tests written for a PropertyShape to use its own targeting rules (aside from the tests where the PropertyShape is the only shape in the test, and it uses sh:targetNode). Additionally, all of the targeting documentation in the SHACL spec shows examples using only sh:NodeShape. It is entirely possible to write a W3C-compliant SHACL validator without the ability for a PropertyShape to gather its own target/focus nodes. Indeed the first versions of PySHACL were released without this feature for more than a year before the omission was reported, because I simply did not know it was possible, and it is rarely used. (Though as you pointed out, I did overlook two examples of its use in other sections of the SHACL spec document.)

In my role as PySHACL maintainer, the only times I see examples of users using standalone PropertyShapes is when they are encountering an issue, and almost always that issue is caused by their incorrect usage of a standalone PropertyShape, where a NodeShape or a nested property shape would be a better fit. That is why I gave the advice I did in issue #160.

As I see it, the biggest danger with having standalone PropertyShapes that select their own targets, is if those shapes are also used elsewhere as the value of a sh:property, for example:

ex:numLeg-shape
  a sh:PropertyShape ;
  sh:targetClass ex:Animal ;
  sh:datatype xsd:integer ;
  sh:nodeKind sh:Literal ;
  sh:path ex:numLeg ;
  .

ex:Bird-shape
  a sh:NodeShape ;
  sh:targetClass ex:Bird ;
  sh:property ex:numLeg-shape ;
  sh:property [
    a sh:PropertyShape ;
    sh:maxInclusive 2 ;
    sh:path ex:numLeg ;
  ] ;
  .

It might not be obvious in this example, the ex:numLeg-shape will be evaluated against ex:Bird instances twice, once from the NodeShape's sh:property constraint, and once from the PropertyShape's sh:targetClass selector.

The other reason I prefer making top-level sh:PropertyShapes is to give the shape an IRI so any SHACL violation reports are easier to reference to a specific shape instance in the ontology.

You can get around this issue by still using reusable named shapes, like this

ex:numLeg-shape
  a sh:PropertyShape ;
  sh:datatype xsd:integer ;
  sh:nodeKind sh:Literal ;
  sh:path ex:numLeg ;
  .

ex:Bird-shape
  a sh:NodeShape ;
  sh:targetClass ex:Bird ;
  sh:property ex:numLeg-shape ;
  sh:property [
    a sh:PropertyShape ;
    sh:maxInclusive 2 ;
    sh:path ex:numLeg ;
  ] ;
  .
ex:Cat-shape
  a sh:NodeShape ;
  sh:targetClass ex:Cat ;
  sh:property ex:numLeg-shape ;
  sh:property [
    a sh:PropertyShape ;
    sh:maxInclusive 4 ;
    sh:path ex:numLeg ;
  ] ;
  .

This gives you re-usability, and Shape identification, without the PropertyShape doing its own targeting. Error reporting using this kind of pattern will be improved after my fix for #169 is merged.

I see value in having sh:PropertyShapes that are independent of sh:NodeShapes.

Yes, you are right, there is value doing it, if it is your preferred style and fits within the ontology and shapes documents you are building, and if you use it correctly and consistently, it is a perfectly valid method of writing your shapes.

The best places to discuss this topic further are the SHACL W3C mailing list, and the SHACL community discord chat server, folks on there will be able to give you much better advice than I can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants