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 3: The Appointment Book

By noon, Dr. Portbridge had seen three patients. She’d scribbled notes on separate napkins, but already she couldn’t remember whether the cat with the limp came before or after the parrot with the cough. She needed a concept for the visit itself, something that ties an animal, a date, and a reason together.


Modeling the appointment


concept Appointment:
  has animal: Animal
  has date: string
  has reason: string
  has diagnosis: string
  has treatment: string

Notice that animal is of type Animal, a reference to the concept we defined earlier. This is how relationships emerge naturally in Dolfin. You don’t need a separate “relationship” syntax for simple ownership; has does the job.

Why is date a string? Good catch. Dolfin 1.0 has four primitive types and doesn’t yet include a built-in date type. For now, we use a string like "2025-01-15". In a later chapter, we’ll see how prefixes and IRIs let you link to external type systems like xsd:date.

Standalone properties

So far, we’ve defined relationships inside concepts using has. But some relationships are important enough to deserve their own definition, especially when they could apply to multiple concepts or when you want to give them metadata.

A property is a standalone, reusable relationship:


property treatedBy:
  Animal -> string

This reads: treatedBy is a relationship from Animal to a string.” The arrow (->) separates the domain (what has the property) from the range (what values it takes).

That string is a placeholder. In reality, we’d want a Veterinarian concept. Let’s make one:


concept Veterinarian:
  has name: string
  has license_number: string

property treatedBy:
  Animal -> Veterinarian

Now treatedBy is a first-class relationship between Animal and Veterinarian, defined separately from either concept.

When to use has vs property

Use caseUse hasUse property
Simple attribute (name, age)
Core part of a concept’s identity
Relationship shared across concepts
Relationship you want to annotate or reason about

Both compile to OWL properties. The difference is readability and intent.

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 Animal:
  has name: string
  has species: string
  has age: int
  has owner: Owner
  
concept Owner:
  has first_name: string
  has last_name: string
  has phone: string

concept Appointment:
  has animal: Animal
  has date: string
  has reason: string
  has diagnosis: string
  has treatment: string

concept Veterinarian:
  has name: string
  has license_number: string

property treatedBy:
  Animal -> Veterinarian

Try it

Add a standalone property owns that goes from Owner to Animal. Then add a property scheduledWith from Appointment to Veterinarian:


concept Animal:
  has name: string
  has species: string
  has age: int
  has owner: Owner
  
concept Owner:
  has first_name: string
  has last_name: string
  has phone: string

concept Appointment:
  has animal: Animal
  has date: string
  has reason: string
  has diagnosis: string
  has treatment: string

concept Veterinarian:
  has name: string
  has license_number: string

property treatedBy:
  Animal -> Veterinarian

# Add your properties here

The appointment book was taking shape. But as Dr. Portbridge typed species: string for the tenth time, she winced. “Dog” could be typed as “dog”, “Dog”, “DOG”, “canine”, or “golden retriever”. A free-text field was an invitation for chaos. She needed a fixed list.