-
Notifications
You must be signed in to change notification settings - Fork 162
Document attributes behavior and gather use cases #388
Description
Currently our FAQ entry on attributes vs slots is a little vague:
In fact there are nuances in use of attributes. Consider:
classes:
C:
attributes:
a:
D:
attributes:
a:The intent of the modeler is not clear here:
- convenient shorthand for not having to use a separate "slots" declaration, "a" is the same slot
- The two "a"s have nothing to do with one another, the purpose of attributes is to insulate
in the above, the internal representation is as follows:
slots:
c1__a:
name: c1__a
from_schema: https://w3id.org/linkml/examples/personinfo
range: my_str
slot_uri: https://w3id.org/linkml/examples/personinfo/a
alias: a
owner: C1
domain_of:
- C1
c2__a:
name: c2__a
from_schema: https://w3id.org/linkml/examples/personinfo
range: my_str
slot_uri: https://w3id.org/linkml/examples/personinfo/a
alias: a
owner: C2
domain_of:
- C2
classes:
C1:
name: C1
definition_uri: https://w3id.org/linkml/examples/personinfo/C1
from_schema: https://w3id.org/linkml/examples/personinfo
slots:
- c1__a
attributes:
a:
name: a
class_uri: https://w3id.org/linkml/examples/personinfo/C1
C2:
name: C2
definition_uri: https://w3id.org/linkml/examples/personinfo/C2
from_schema: https://w3id.org/linkml/examples/personinfo
slots:
- c2__a
attributes:
a:
name: a
class_uri: https://w3id.org/linkml/examples/personinfo/C2so-called mangled names are created for induced slots. The two attributes are completely isolated. They could have radically different semantics.
However -- note that the two slots have the same slot_uri https://w3id.org/linkml/examples/personinfo/a. This is important later.
When the generator translates to formalisms where fields are not first class entities (Python dataclasses; JSON-Schema; SQL; ...) then things are "de-mangled". The alias field is used to construct field definitions for each class. This is safe because slots are not first class, and the name of a field in one class can be the same as the name as a field in another class.
as an example, here is json-schema from the above:
{
"$defs": {
"C1": {
"additionalProperties": false,
"description": "",
"properties": {
"a": {
"type": "string"
}
},
"required": [],
"title": "C1",
"type": "object"
},
"C2": {
"additionalProperties": false,
"description": "",
"properties": {
"a": {
"type": "string"
}
},
"required": [],
"title": "C2",
"type": "object"
}
},
"$id": "https://w3id.org/linkml/examples/personinfo",
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": true,
"properties": {},
"title": "personinfo",
"type": "object"
}There is no generic "a" entry, c1__a and c2__a can both be represented by "a"s within the context of their containing class object
It gets trickier when translating to formalisms where slots are first class. Slots are first class in RDF/OWL. They can also be considered first-class in our markdown serialization, as we make pages for them. However, things are a little inconsistent here.
json-ld-context:
{
"_comments": "Auto generated from linkml_issue_388.yaml by jsonldcontextgen.py version: 0.1.1\n Generation date: 2021-09-30 14:49\n Schema: personinfo\n \n id: https://w3id.org/linkml/examples/personinfo\n description: \n license: \n ",
"@context": {
"linkml": "https://w3id.org/linkml/",
"personinfo": "https://w3id.org/linkml/examples/personinfo/",
"@vocab": "https://w3id.org/linkml/examples/personinfo/",
"a": {
"@type": "xsd:string",
"@id": "https://w3id.org/linkml/examples/personinfo/a"
},
"C1": {
"@id": "https://w3id.org/linkml/examples/personinfo/C1"
},
"C2": {
"@id": "https://w3id.org/linkml/examples/personinfo/C2"
}
}
}This is consistent with the json, which uses fields like "a", not "C1__a". The mapping to URI is consistent with slot_uri in the internal representation
E.g if we have data
@type: pi:C1
a: foo(I realize now "a" was maybe a bad choice of name - it is not intended to be the same as the keyword 'a' in RDF!)
this translates to
[ rdf:type pi:C1 ;
pi:a "foo"]so far so good; but note there is a potential for OWL errors here is the two "a"s map to an OP and a DP respectively
if we use gen-rdf we get:
<https://w3id.org/linkml/examples/personinfo/c1__a> a linkml:SlotDefinition ;
skos:inScheme <https://w3id.org/linkml/examples/personinfo> ;
linkml:alias "a" ;
linkml:domain_of <https://w3id.org/linkml/examples/personinfo/C1> ;
linkml:owner <https://w3id.org/linkml/examples/personinfo/C1> ;
linkml:range <https://w3id.org/linkml/examples/personinfo/my_str> ;
linkml:slot_uri <https://w3id.org/linkml/examples/personinfo/a> .
<https://w3id.org/linkml/examples/personinfo/c2__a> a linkml:SlotDefinition ;
skos:inScheme <https://w3id.org/linkml/examples/personinfo> ;
linkml:alias "a" ;
linkml:domain_of <https://w3id.org/linkml/examples/personinfo/C2> ;
linkml:owner <https://w3id.org/linkml/examples/personinfo/C2> ;
linkml:range <https://w3id.org/linkml/examples/personinfo/my_str> ;
linkml:slot_uri <https://w3id.org/linkml/examples/personinfo/a> .
Note that the instances of SlotDefinition are not the same URIs that are used in the actual data triples. If we wanted to do something like validation using the rdf schema model we would traverse from the slot_uri checking against the linkml:Domain
This poses the question of what kind of thing is personinfo:a? It is clearly a property. But given we are using the same URI it implies there is some commonality between C1__a and C2__a, enough to name a URI. From OWL-DL usage it implies that it must be of the same OWL type (object vs data property). Note that domains and ranges cannot propagate up as intersections, only as unions.
I propose that we recommend that we don't assuming attributes of. the same name are completely insulated from eachother, that we better document that these have the same URI (unless explicitly overridden with slot_uri), and we proactively check that the same slot_uri doesn't imply different DL types
[in progress, will edit more later]