Chapter 7: The Specialist Problem
Dr. Reyes could do everything Dr. Portbridge could, checkups, vaccinations, prescriptions. Plus surgery. Modeling him as a plain
Veterinarianwould lose the surgery part. Creating a completely separateSurgeonconcept would duplicate all the shared fields. Neither option felt right.
The problem
You have two kinds of things that share most of their structure but differ in some aspects. Duplicating attributes across concepts is fragile: change one, forget the other. You need inheritance.
#sub: concept inheritance
concept Veterinarian:
has name: one string
has license_number: one string
has specialization: optional string
concept Surgeon:
sub Veterinarian
has surgery_count: one int
has certified_procedures: at least 1 string
The keyword sub says: “A Surgeon is a specialization of Veterinarian.” A Surgeon automatically has name, license_number, and specialization (inherited from Veterinarian), plus its own surgery_count and certified_procedures.
In OWL terms, this is rdfs:subClassOf. In object-oriented terms, it’s inheritance. In plain English: every Surgeon is a Veterinarian, but not every Veterinarian is a Surgeon.
Building a hierarchy
Let’s extend this further. The clinic is growing and has different roles:
concept Veterinarian:
has name: one string
has license_number: one string
has specialization: optional string
concept Surgeon:
sub Veterinarian
has surgery_count: one int
has certified_procedures: at least 1 string
concept Dentist:
sub Veterinarian
has dental_certification: one string
concept Intern:
sub Veterinarian
has university: one string
has year: one int
All four concepts share name, license_number, and specialization. Each adds its own details.
Inheritance for animals too
The Species enum tells us what kind of animal something is, but it doesn’t let us attach species-specific attributes. With inheritance, we can:
concept Animal:
has name: one string
has species: one Species
has age: optional int
has weight: optional float
has owner: optional Owner
has vaccinations: Vaccination
has allergies: string
concept Dog:
sub Animal
has breed: optional string
has neutered: one boolean
concept Cat:
sub Animal
has indoor: one boolean
concept Bird:
sub Animal
has wingspan: optional float
has can_fly: one boolean
A Dog is an Animal with breed and neuter status. A Bird is an Animal with a wingspan and flight ability. The shared core (name, species, age, etc.) is defined once and inherited everywhere.
Ordering convention
Inside a concept body, put sub first, then has declarations. This isn’t enforced by the parser, but it’s the standard Dolfin style:
concept Surgeon:
sub Veterinarian # ← parent first
has surgery_count: one int # ← then own attributes
The story so far
package <http://happypaws.com/clinic>:
dolfin_version "1"
version "0.1.0"
author "Dr. Helen Portbridge"
description "The Happy Paws veterinary clinic data model"
concept Species:
one of:
Dog
Cat
Bird
Rabbit
Reptile
Other
concept Urgency:
one of:
Routine
Urgent
Emergency
concept AppointmentStatus:
one of:
Scheduled
InProgress
Completed
Cancelled
concept Owner:
has first_name: one string
has last_name: one string
has phone_numbers: at least 1 string
has email: optional string
has address: optional string
concept Veterinarian:
has name: one string
has license_number: one string
has specialization: optional string
concept Surgeon:
sub Veterinarian
has surgery_count: one int
has certified_procedures: at least 1 string
concept Dentist:
sub Veterinarian
has dental_certification: one string
concept Intern:
sub Veterinarian
has university: one string
has year: one int
concept Vaccination:
has vaccine_name: one string
has date_administered: one string
has batch_number: optional string
concept Animal:
has name: one string
has species: one Species
has age: optional int
has weight: optional float
has owner: optional Owner
has vaccinations: Vaccination
has allergies: string
concept Dog:
sub Animal
has breed: optional string
has neutered: one boolean
concept Cat:
sub Animal
has indoor: one boolean
concept Bird:
sub Animal
has wingspan: optional float
has can_fly: one boolean
concept Appointment:
has animal: one Animal
has date: one string
has reason: one string
has urgency: one Urgency
has status: one AppointmentStatus
has diagnosis: optional string
has treatments: string
has notes: optional string
property treatedBy:
Animal -> Veterinarian
Try it
The clinic just hired a Radiologist (a specialized Veterinarian). They have a machine_certified_on attribute (a string, the machine name) and a readings_performed count. Add the concept:
concept Veterinarian:
has name: one string
has license_number: one string
has specialization: optional string
concept Surgeon:
sub Veterinarian
has surgery_count: one int
has certified_procedures: at least 1 string
# Add Radiologist here
The model now captured the difference between Dr. Portbridge (general practice), Dr. Reyes (surgeon), and the new dental specialist who started on Tuesdays. But Dr. Portbridge noticed something: she was manually checking whether each animal’s vaccinations were up to date, whether appointments with surgeons involved surgical cases, whether overdue checkups needed follow-up reminders. She was the reasoning engine, and she was exhausted.
“What if the system could figure these things out by itself?”