- Updated ranges for multiple slots from `string` to `uriorcurie` to address OWL "Ambiguous type" warnings and allow for URI/CURIE references. - Removed specialized slots for subtitle and transcript formats, consolidating them under broader predicates. - Introduced new slots for structured descriptions, observation source documents, and entity statuses to improve data modeling. - Implemented Rule 54 to broaden generic predicate ranges instead of creating bespoke predicates, promoting schema reuse and reducing complexity. - Added a script for generating OWL ontology with type-object handling to ensure consistent ObjectProperty treatment for polymorphic slots.
179 lines
5.4 KiB
Markdown
179 lines
5.4 KiB
Markdown
# Rule 54: Broaden Generic Predicate Ranges Instead of Creating Bespoke Predicates
|
|
|
|
🚨 **CRITICAL**: When fixing gen-owl "Ambiguous type" warnings, **broaden the range of generic predicates** rather than creating specialized bespoke predicates.
|
|
|
|
## The Problem
|
|
|
|
gen-owl "Ambiguous type" warnings occur when a slot is used as both:
|
|
- **DatatypeProperty** (base range: `string`, `integer`, `uri`, etc.)
|
|
- **ObjectProperty** (slot_usage override range: a class like `Description`, `SubtitleFormatEnum`)
|
|
|
|
This creates OWL ambiguity because OWL requires properties to be either DatatypeProperty OR ObjectProperty, not both.
|
|
|
|
## ❌ WRONG Approach: Create Bespoke Predicates
|
|
|
|
```yaml
|
|
# DON'T DO THIS - creates proliferation of rare-use predicates
|
|
slots:
|
|
has_or_had_subtitle_format: # Only used by VideoSubtitle
|
|
range: SubtitleFormatEnum
|
|
has_or_had_transcript_format: # Only used by VideoTranscript
|
|
range: TranscriptFormat
|
|
```
|
|
|
|
**Why This Is Wrong**:
|
|
- Creates **predicate proliferation** (schema bloat)
|
|
- Bespoke predicates are **rarely reused** across classes
|
|
- **Increases cognitive load** for schema users
|
|
- **Fragments the ontology** unnecessarily
|
|
- Violates the principle of schema parsimony
|
|
|
|
## ✅ CORRECT Approach: Broaden Generic Predicate Ranges
|
|
|
|
```yaml
|
|
# DO THIS - make the generic predicate flexible enough
|
|
slots:
|
|
has_or_had_format:
|
|
range: uriorcurie # Broadened from string
|
|
description: |
|
|
The format of a resource. Classes narrow this to specific
|
|
enum types (SubtitleFormatEnum, TranscriptFormatEnum) via slot_usage.
|
|
```
|
|
|
|
Then in class files, use `slot_usage` to narrow the range:
|
|
|
|
```yaml
|
|
classes:
|
|
VideoSubtitle:
|
|
slots:
|
|
- has_or_had_format
|
|
slot_usage:
|
|
has_or_had_format:
|
|
range: SubtitleFormatEnum # Narrowed for this class
|
|
required: true
|
|
```
|
|
|
|
## Range Broadening Options
|
|
|
|
| Original Range | Broadened Range | When to Use |
|
|
|----------------|-----------------|-------------|
|
|
| `string` | `uriorcurie` | When class overrides use URI-identified types or enums |
|
|
| `string` | `Any` | When truly polymorphic (strings AND class instances) |
|
|
| Specific class | Common base class | When multiple subclasses are used |
|
|
|
|
## Decision Tree
|
|
|
|
```
|
|
gen-owl warning: "Ambiguous type for: SLOTNAME"
|
|
↓
|
|
Is base slot range a primitive (string, integer, uri)?
|
|
├─ YES → Broaden to uriorcurie or Any
|
|
│ - Edit modules/slots/SLOTNAME.yaml
|
|
│ - Change range: string → range: uriorcurie
|
|
│ - Document change with Rule 54 reference
|
|
│ - Keep class-level slot_usage overrides (they narrow the range)
|
|
│
|
|
└─ NO → Consider if base slot needs common ancestor class
|
|
- Create abstract base class if needed
|
|
- Or broaden to uriorcurie
|
|
```
|
|
|
|
## Implementation Workflow
|
|
|
|
1. **Identify warning**: `gen-owl ... 2>&1 | grep "Ambiguous type for:"`
|
|
|
|
2. **Check base slot range**:
|
|
```bash
|
|
cat modules/slots/SLOTNAME.yaml | grep -A5 "^slots:" | grep "range:"
|
|
```
|
|
|
|
3. **Find class overrides**:
|
|
```bash
|
|
for f in modules/classes/*.yaml; do
|
|
grep -l "SLOTNAME" "$f" && grep -A3 "SLOTNAME:" "$f" | grep "range:"
|
|
done
|
|
```
|
|
|
|
4. **Broaden base range**:
|
|
- Edit `modules/slots/SLOTNAME.yaml`
|
|
- Change `range: string` → `range: uriorcurie`
|
|
- Add annotation documenting the change
|
|
|
|
5. **Verify fix**: Run gen-owl and confirm warning is gone
|
|
|
|
6. **Keep slot_usage overrides**: Class-level range narrowing is fine and expected
|
|
|
|
## Examples
|
|
|
|
### Example 1: has_or_had_format
|
|
|
|
**Before (caused warning)**:
|
|
```yaml
|
|
# Base slot
|
|
slots:
|
|
has_or_had_format:
|
|
range: string # DatatypeProperty
|
|
|
|
# Class override
|
|
classes:
|
|
VideoSubtitle:
|
|
slot_usage:
|
|
has_or_had_format:
|
|
range: SubtitleFormatEnum # ObjectProperty → CONFLICT!
|
|
```
|
|
|
|
**After (fixed)**:
|
|
```yaml
|
|
# Base slot - broadened
|
|
slots:
|
|
has_or_had_format:
|
|
range: uriorcurie # Now ObjectProperty-compatible
|
|
|
|
# Class override - unchanged, still narrows
|
|
classes:
|
|
VideoSubtitle:
|
|
slot_usage:
|
|
has_or_had_format:
|
|
range: SubtitleFormatEnum # Valid narrowing
|
|
```
|
|
|
|
### Example 2: has_or_had_hypernym
|
|
|
|
**Before**: `range: string` (DatatypeProperty)
|
|
**After**: `range: uriorcurie` (ObjectProperty-compatible)
|
|
|
|
Classes that override to class ranges now work without ambiguity.
|
|
|
|
## Validation
|
|
|
|
After broadening, run:
|
|
```bash
|
|
gen-owl 01_custodian_name_modular.yaml 2>&1 | grep "Ambiguous type for: SLOTNAME"
|
|
```
|
|
|
|
The warning should disappear without creating new predicates.
|
|
|
|
## Anti-Patterns to Avoid
|
|
|
|
| ❌ Anti-Pattern | ✅ Correct Pattern |
|
|
|----------------|-------------------|
|
|
| Create `has_or_had_subtitle_format` | Broaden `has_or_had_format` to `uriorcurie` |
|
|
| Create `has_or_had_entity_type` | Broaden `has_or_had_type` to `uriorcurie` |
|
|
| Create `has_or_had_X_label` | Broaden `has_or_had_label` to `uriorcurie` |
|
|
| Create `has_or_had_X_status` | Broaden `has_or_had_status` to `uriorcurie` |
|
|
|
|
## Rationale
|
|
|
|
This approach:
|
|
1. **Reduces schema complexity** - Fewer predicates to understand
|
|
2. **Promotes reuse** - Generic predicates work across domains
|
|
3. **Maintains OWL consistency** - Single property type per predicate
|
|
4. **Preserves type safety** - slot_usage still enforces class-specific ranges
|
|
5. **Follows semantic web best practices** - Broad predicates, narrow contexts
|
|
|
|
## See Also
|
|
|
|
- Rule 38: Slot Centralization and Semantic URI Requirements
|
|
- Rule 39: Slot Naming Convention (RiC-O Style)
|
|
- Rule 49: Slot Usage Minimization
|
|
- LinkML Documentation: [slot_usage](https://linkml.io/linkml-model/latest/docs/slot_usage/)
|