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. The first patient of the morning — Biscuit, the nervous golden retriever — 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.