rdfs:Class vs owl:Thing

I've been trying to decide whether to use foaf:Group or dcterms:AgentClass in an ontology. I had assumed that they were equivalent, but I've been told that as foaf:Group is a subclass of owl:Thing and dcterms:AgentClass is a subclass of rdfs:Class, they are different.

I had thought that owl:Thing was the superclass of all classes, but it seems this is not the case. So, how do I work out whether a specific group in my ontology should be a subclass of foaf:Group or dcterms:AgentClass?

owl:Thing is the class that contains individuals (things).

In OWL Direct (DL-Based) Semantics, this excludes classes and properties.

In OWL RDF Based semantics, this includes everything, where classes and properties are also viewed as individuals (and thus owl:Thing has the same extension as rdfs:Resource).

rdfs:Class is the (RDFS) class of classes. Any member is itself a class.

foaf:Group being a subclass of owl:Thing means that any instance of foaf:Group is also an owl:Thing. In Direct Semantics, it means that the foaf:Group instances are individuals. In RDF-based semantics, foaf:Group being a subclass of owl:Thing means pretty much nothing—instances can be classes, properties or individuals. To define a group, I use:

ex:URQ rdf:type foaf:Group .

...where URQ is the research unit I "work" in. To give a member of the group, I say:

ex:URQ foaf:member ex:Aidan .

I can also use the property foaf:membershipClass to relate a foaf:Group and a class of individuals that constitute the group (as definable through standard RDFS/OWL machinery).

ex:URQ foaf:membershipClass [ owl:oneOf (ex:Aidan, ex:Juergen, ... ex:Axel ) ] .


ex:URQ foaf:membershipClass [ owl:hasValue ex:Axel . owl:onProperty ex:supervisor ] .

However, FOAF don't define the machinery to infer that an instance of the respective membership-class is a member of the respective FOAF group (e.g., ex:Aidan ex:supervisor ex:Axel will not directly infer ex:Aidan foaf:member ex:URQ through RDFS/OWL semantics).

dcterms:AgentClass being a subclass of rdfs:Class means that any instance of the former is also a member of rdfs:Class. In particular, this indicates a certain kind of intended usage which gets rid of the indirection used in FOAF. Let's say that we have a class

ex:URQ rdf:type dcterms:AgentClass .

Now, ex:URQ is itself class. So, then I can give an instance of ex:URQ as follows:

ex:Aidan rdf:type ex:URQ .

I use the standard RDF/RDFS machinery for doing this. I can then define ex:URQ as if it were a class. If I want to define "sub-groups", I can use:

ex:URQ rdfs:subClassOf ex:DERI .

where DERI is my institute. Through standard RDF semantics, I can then infer that:

ex:Aidan rdf:type ex:DERI .

Or, taking an example as above, I can say that:

ex:URQ owl:equivalentClass [ owl:hasValue ex:Axel . owl:onProperty ex:supervisor ] .

Now, OWL semantics will infer ex:Aidan ex:supervisor ex:Axel from ex:Aidan rdf:type ex:URQ and vice-versa.

Which you choose is up to you, but hopefully you've a better idea of how both work.

EDIT: Also worth noting that dcterms:AgentClass is not defined to be a subclass of rdf:Class, though it's hinted at by the fact that the dcterms:Agent class is of type dcterms:AgentClass.

EDIT2: From your comment, you want to create a class ex:Audience to represent the general notion of an audience as a group of people (I guess?). You suggest:

ex:Audience rdfs:subClassOf foaf:Group .

seems sound enough in that you're just saying that each instance of ex:Audience is also an instance of foaf:Group which intuitively makes sense. You also suggest:

ex:Audience rdf:type dcterms:AgentClass .

which doesn't seem quite right. To me, a specific audience would be a dcterms:AgentClass, so again I would say instead:

ex:Audience rdfs:subClassOf dcterms:AgentClass .

now, every instance of ex:Audience is an instance of dcterms:AgentClass, and so, (informally... as DC intends) each audience is itself a class, and a subclass of dc:Agent.

If you want to stay compatible with both, the problem then comes when you want to start declaring instances of audience and who was in them, which requires a duplication of effort.

ex:SomeAudience rdf:type ex:Audience .
ex:Person1 rdf:type ex:SomeAudience .
ex:SomeAudience foaf:member ex:Person1 .

Easiest way around this would be to define properties ex:hasAudienceMember and ex:inAudience and say the following:

ex:hasAudienceMember rdfs:domain ex:Audience .
ex:hasAudienceMember rdfs:range foaf:Person .
ex:hasAudienceMember rdfs:subPropertyOf foaf:member .
ex:hasAudienceMember owl:inverseOf ex:inAudience .
ex:inAudience rdfs:subPropertyOf rdf:type .

Now given the above, and this from before:

ex:Audience rdfs:subClassOf foaf:Group .
ex:Audience rdfs:subClassOf dcterms:AgentClass .

We can state members of the audience as follows:

ex:Person1 ex:inAudience ex:SomeAudience .

Which will give us:

ex:Person1 rdf:type ex:SomeAudience .
ex:Person1 rdf:type foaf:Person .
ex:Person1 rdf:type foaf:Agent . # inc. FOAF
ex:Person1 rdf:type dc:Agent . # inc. FOAF
ex:SomeAudience rdf:type dcterms:AgentClass .
ex:SomeAudience rdf:type foaf:Group .
ex:SomeAudience ex:hasAudienceMember ex:Person1 .
ex:SomeAudience foaf:member ex:Person1 .

You can go a little further as you see fit. To support foaf:membershipClass with the above modelling, you could use:

ex:Audience rdfs:subClassOf [ owl:onProperty foaf:membershipClass ; owl:hasSelf true .] .

which is a little weird, but says that each instance of audience has itself as a foaf:membershipClass. (TBH, I wouldn't bother... you'll end up with boring reflexive foaf:membershipClass triples whose semantics will buy you nothing at the moment.)

To say that all audiences are subclasses of foaf:Person, you could say something like:

ex:Audience rdfs:subClassOf [ owl:onProperty rdfs:subClassOf ; owl:hasValue foaf:Person .]

again, I wouldn't be too bothered... up to you.

Finally, AFAIK some parts of the above definitions are OWL Full, in that they have use the restricted vocabulary (?) in atypical positions (e.g., rdfs:subClassOf, rdf:type outside of the predicate position). You probably won't need all that stuff, or even to extend both FOAF and DC, but you can pick, choose and tweak as you see fit.

[Man, anything to avoid working on my PhD :)]

For those who are concerned about keeping their ontologies in OWL DL, notice that in OWL 2 there is a new mechanism called punning. This mechanism allows one to use a class as an individual. So, the following is valid OWL 2 DL (borrowing Aidan's example):

ex:URQ  rdf:type  foaf:Group, owl:Class ;
        foaf:member  ex:Aidan, ex:Juergen, ..., ex:Axel ;
        owl:oneOf  ( ex:Aidan ex:Juergen ... ex:Axel ) ;
        rdfs:subClassOf  ex:DERI .

However, it won't make the connection between the foaf:member of a foaf:Group and the instances of the corresponding rdfs:Class. So you have to maintain the foaf:member with a separate mechanism. But this can be easily automated by taking all the instances of the class and adding a foaf:member relation for each of them, for instance with a CONSTRUCT query:

CONSTRUCT {  ?group  foaf:member  ?person . }
     ?group  a  foaf:Group, owl:Class .
     ?person a  ?group .

One may need some reasoning to infer the instances of ?group in order to not miss one.