Chapter 9: Guard Rails
An owner brought in a hamster for dental surgery. The system accepted the appointment without complaint, it didn’t know that dental procedures require a Dentist, or that hamsters aren’t in the Species enum (they’d have to be
Other). The rules from the previous chapter could flag problems after the fact, but Dr. Portbridge wanted to prevent mistakes from being recorded in the first place.
The problem
Rules infer new facts, they add information. But they don’t prevent bad data. We need a way to express expectations: “Every X must satisfy Y”, “No X should ever have Z.”
Quantifiers in rules
Dolfin provides quantifiers that express conditions over collections:
| Quantifier | Meaning |
|---|---|
all | Every element must satisfy the condition |
none | No element may satisfy the condition |
at_least n | At least n elements must satisfy it |
at_most n | At most n elements may satisfy it |
exactly n | Exactly n elements must satisfy it |
Ensuring surgical appointments have surgeons
concept InvalidSurgery
rule validate_surgery_staff:
match:
?appt a Appointment
?appt reason "surgery"
among:
?appt animal [ treatedBy ?vet ]
none:
?vet a Surgeon
then:
?appt a InvalidSurgery
This reads: “If an appointment is for surgery but the treating vet is NOT a Surgeon, flag it as invalid.”
The none ?vet a Surgeon pattern succeeds when the bound ?vet does not belong to the Surgeon concept.
Minimum vaccination rules
Every dog should have at least one vaccination (in the real world, rabies is required by law in most places):
concept UnderVaccinatedDog
rule check_dog_vaccines:
match:
?dog a Dog
?dog vaccinations 0
then:
?dog a UnderVaccinatedDog
Comparison operators
You’ve already seen > and = in rules. Here’s the complete set:
| Operator | Meaning | Example |
|---|---|---|
= | Equal | ?x = 5 |
!= | Not equal | ?x != "none" |
< | Less than | ?age < 1 |
<= | Less than or equal | ?age <= 12 |
> | Greater than | ?weight > 40.0 |
>= | Greater than or equal | ?age >= 10 |
Combining conditions
Rule patterns are combined with implicit AND. All conditions must hold for the rule to fire:
concept AtRiskAnimal
rule flag_at_risk:
match:
?animal a Animal
?animal age [ > 15 ]
?animal weight [ < 2.0 ]
?animal vaccinations 0
then:
?animal a AtRiskAnimal
“An animal older than 15, weighing less than 2kg, with no vaccinations, is at risk.”
Validation concepts as a pattern
Notice the design pattern emerging: we create empty concepts (InvalidSurgery, UnderVaccinatedDog, AtRiskAnimal) that exist only to be assigned by rules. They act as tags or flags. Downstream systems can query for all instances of InvalidSurgery and take action.
This is a powerful idiom:
- Define an empty concept that represents a condition
- Write a rule that classifies instances into that concept
- Query or display instances of the condition
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:
only values:
Dog
Cat
Bird
Rabbit
Reptile
Other
concept Urgency:
only values:
Routine
Urgent
Emergency
concept AppointmentStatus:
only values:
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
has preferred_vet: optional Veterinarian
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
# Derived / flag concepts
concept UnvaccinatedAnimal
concept UnsafeAssignment
concept OverweightAnimal
concept SeniorCat
concept InvalidSurgery
concept UnderVaccinatedDog
concept AtRiskAnimal
# Inference rules
rule flag_unvaccinated:
match:
?animal a Animal
?animal vaccinations 0
then:
?animal a UnvaccinatedAnimal
rule flag_intern_emergency:
match:
?appt a Appointment
?appt urgency Emergency
?appt animal [ treatedBy [ a Intern ] ]
then:
?appt a UnsafeAssignment
rule flag_overweight_dog:
match:
?dog a Dog
?dog weight [ > 40.0 ]
then:
?dog a OverweightAnimal
rule flag_senior_cat:
match:
?cat a Cat
?cat age [ >= 10 ]
then:
?cat a SeniorCat
rule assign_primary_vet:
match:
?animal a Animal
?animal owner [ preferred_vet ?vet ]
then:
?animal treatedBy ?vet
rule validate_surgery_staff:
match:
?appt a Appointment
?appt reason "surgery"
among:
?appt animal [ treatedBy ?vet ]
none:
?vet a Surgeon
then:
?appt a InvalidSurgery
rule check_dog_vaccines:
match:
?dog a Dog
?dog vaccinations 0
then:
?dog a UnderVaccinatedDog
rule flag_at_risk:
match:
?animal a Animal
?animal age [ > 15 ]
?animal weight [ < 2.0 ]
?animal vaccinations 0
then:
?animal a AtRiskAnimal
Try it
Write a validation rule that flags an Appointment as MissingDiagnosis when its status is Completed but diagnosis is absent (equals empty string ""):
concept MissingDiagnosis:
rule check_completed_diagnosis:
match:
# your patterns here
then:
# your assertion here
The guard rails caught two problems on day one: a surgery booked with an intern and a completed appointment with no recorded diagnosis. Dr. Portbridge felt like the system was finally earning its keep.
Then the regional veterinary board called. They needed the clinic to export its data in a format compatible with the national animal health registry, which used standard OWL/RDF vocabularies. Dr. Portbridge’s concept names were fine for her clinic, but the outside world expected specific IRIs. She needed a bridge.