Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 8: Populating the Clinic

The model had grown up. Dr. Portbridge now had concepts for animals, owners, vets, and appointments, with cardinality constraints, enums, and a full inheritance hierarchy. But the system was still abstract: no actual dogs, no actual owners, no actual appointments. Biscuit, the nervous golden retriever, the first patient of the morning, was sitting in the waiting room. It was time to enter some real data.


The problem

So far, your Dolfin file contains descriptions of categories: what an Animal looks like, what an Appointment requires. It contains no individuals: no specific dog named Biscuit, no actual appointment on a Tuesday afternoon. This is the gap between a schema and a database.

Dolfin fills this gap with facts.

Your first fact


fact DrPortbridge a Veterinarian
  name "Helen Portbridge"
  license_number "VET-2025-001"

The structure mirrors the schema:

  • fact introduces an instance declaration.
  • DrPortbridge is the instance’s identifier, you can reference it elsewhere as :DrPortbridge.
  • a Veterinarian asserts that this instance is of type Veterinarian.
  • The indented block lists property assertions, one per line: the property name, then the value.

This is the same indentation-based style you already know from concept, rule, and package declarations. No new punctuation to learn.

Primitive values

Facts support all the same primitive types you use in has declarations:


fact Biscuit a Dog
  name "Biscuit"
  species Dog
  age 5
  weight 32.4
  neutered true
  allergies "pollen"
Value kindExample
String"Biscuit"
Integer5
Float32.4
Booleantrue
Enum valueDog (no quotes)

Enum values are written without quotes; the parser resolves them against the property’s declared type.

Referencing other facts

When a property’s type is a concept (not a primitive), the value is a reference to another fact, written with a leading colon:


fact JohnSmith a Owner
  first_name "John"
  last_name "Smith"
  phone_numbers "555-1234"
  preferred_vet :DrPortbridge

fact Biscuit a Dog
  name "Biscuit"
  species Dog
  age 5
  weight 32.4
  neutered true
  owner :JohnSmith

:DrPortbridge and :JohnSmith refer to the DrPortbridge and JohnSmith facts declared elsewhere in the file. Order doesn’t matter. The parser resolves identifiers after reading the whole file, so forward references work.

Anonymous blocks

Some properties hold a value that is a structured object rather than a primitive or a reference. Biscuit’s vaccination record is a good example: it’s a Vaccination instance that only belongs to Biscuit, with no need for its own global identifier.

Use an anonymous block (square brackets) for this:


fact Biscuit a Dog
  name "Biscuit"
  species Dog
  age 5
  weight 32.4
  neutered true
  owner :JohnSmith
  vaccinations [
    vaccine_name "Rabies"
    date_administered "2024-03-15"
    batch_number "RB-2024-0042"
  ]

The block’s type (Vaccination) is inferred from the property’s declared range. If the range is an abstract parent concept and you need to specify a subtype, you can add a SubType as the first line of the block:


animal [
  a Dog
  name "Rex"
]

Multi-valued properties

For properties with cardinality some, any, or at least N, simply repeat the property name on multiple lines:


fact JohnSmith a Owner
  first_name "John"
  last_name "Smith"
  phone_numbers "555-1234"
  phone_numbers "555-5678"
  preferred_vet :DrPortbridge

Or write them as a comma-separated list:


fact JohnSmith a Owner
  first_name "John"
  last_name "Smith"
  phone_numbers "555-1234", "555-5678"
  preferred_vet :DrPortbridge

Both forms are equivalent. The first reads like structured records; the second saves space when the values are short.

Multiple anonymous blocks work the same way. Biscuit received three vaccines:


fact Biscuit a Dog
  name "Biscuit"
  species Dog
  age 5
  weight 32.4
  neutered true
  owner :JohnSmith
  vaccinations [
    vaccine_name "Rabies"
    date_administered "2024-03-15"
  ]
  vaccinations [
    vaccine_name "Distemper"
    date_administered "2024-03-15"
  ]
  vaccinations [
    vaccine_name "Bordetella"
    date_administered "2024-03-15"
  ]

Multiple types

A fact can assert membership in more than one concept, separated by commas:


fact Pixel a Cat, UnvaccinatedAnimal
  name "Pixel"
  species Cat
  indoor true

This is useful when an instance simultaneously belongs to a base concept and a flag concept. Here, Pixel is both a Cat and (currently) an UnvaccinatedAnimal. In practice, flag concepts like UnvaccinatedAnimal are usually derived by rules rather than asserted directly, but the syntax supports both.

A day at the clinic

Here are the facts for opening day at Happy Paws:


# --- Staff ---

fact DrPortbridge a Veterinarian
  name "Helen Portbridge"
  license_number "VET-2025-001"

fact DrReyes a Surgeon
  name "Carlos Reyes"
  license_number "VET-2025-042"
  surgery_count 0
  certified_procedures "Soft tissue"
  certified_procedures "Orthopaedic"

# --- Owners ---

fact JohnSmith a Owner
  first_name "John"
  last_name "Smith"
  phone_numbers "555-1234"
  preferred_vet :DrPortbridge

# --- Animals ---

fact Biscuit a Dog
  name "Biscuit"
  species Dog
  age 5
  weight 32.4
  neutered true
  owner :JohnSmith
  vaccinations [
    vaccine_name "Rabies"
    date_administered "2024-03-15"
  ]
  vaccinations [
    vaccine_name "Distemper"
    date_administered "2024-03-15"
  ]
  vaccinations [
    vaccine_name "Bordetella"
    date_administered "2024-03-15"
  ]

fact Pixel a Cat
  name "Pixel"
  species Cat
  indoor true

# --- Appointments ---

fact Appt001 a Appointment
  animal :Biscuit
  date "2025-01-15"
  reason "annual checkup"
  urgency Routine
  status Completed
  diagnosis "Healthy"
  treatments "Bordetella booster"

Notice:

  • :Biscuit in the appointment refers back to the Biscuit fact.
  • Pixel has no owner, she’s the stray kitten. The optional Owner cardinality allows this.
  • urgency Routine and status Completed are enum values without quotes.

The story so far


# ============================================================
#  Happy Paws: Schema + Opening Day Facts
# ============================================================

package <http://happypaws.com/clinic>:
  dolfin_version "1"
  version "1.0.0"
  author "Dr. Helen Portbridge"
  description "The Happy Paws veterinary clinic data model"

# --- Schema (chapters 1–7) ---

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
  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 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 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

# --- Facts (chapter 8) ---

fact DrPortbridge a Veterinarian
  name "Helen Portbridge"
  license_number "VET-2025-001"

fact DrReyes a Surgeon
  name "Carlos Reyes"
  license_number "VET-2025-042"
  surgery_count 0
  certified_procedures "Soft tissue"
  certified_procedures "Orthopaedic"

fact JohnSmith a Owner
  first_name "John"
  last_name "Smith"
  phone_numbers "555-1234"
  preferred_vet :DrPortbridge

fact Biscuit a Dog
  name "Biscuit"
  species Dog
  age 5
  weight 32.4
  neutered true
  owner :JohnSmith
  vaccinations [
    vaccine_name "Rabies"
    date_administered "2024-03-15"
  ]
  vaccinations [
    vaccine_name "Distemper"
    date_administered "2024-03-15"
  ]
  vaccinations [
    vaccine_name "Bordetella"
    date_administered "2024-03-15"
  ]

fact Pixel a Cat
  name "Pixel"
  species Cat
  indoor true

fact Appt001 a Appointment
  animal :Biscuit
  date "2025-01-15"
  reason "annual checkup"
  urgency Routine
  status Completed
  diagnosis "Healthy"
  treatments "Bordetella booster"

Try it

Register a new patient: a rabbit named Clover, owned by Maria Garcia (phone "555-9900"). Clover is 3 years old, weighs 1.8 kg, and has had one vaccination ("RHDV2", administered "2024-11-01").


fact MariaGarcia a Owner
  first_name "Maria"
  last_name "Garcia"
  phone_numbers "555-9900"

# Add Clover here

The system was no longer just a blueprint. It knew who worked at Happy Paws, who owned which animals, which vaccines had been given, and which appointments had been completed. Biscuit’s record showed three vaccinations and a clean checkup. Pixel’s record showed none at all.

Dr. Portbridge stared at Pixel’s entry. She was manually scanning for unvaccinated animals, overdue checkups, interns assigned to emergencies. Every check was a thing she had to remember to do. “What if the system could notice these things automatically?” she wondered. She needed the system to reason, to look at the facts and draw its own conclusions.