Type System
Every attribute (has) and property range in Dolfin has a type. A type is
one of:
- a primitive (
string,int,float,boolean), - a temporal type (
date,time,date_time,duration), - a quantity type (
mass,length,speed, …), - a concept you declared, or
- a closed concept (
one of).
Types are what appears after the : in a has declaration or on either side of
a property’s ->:
concept Person:
has name: one string
has born: optional date
has employer: optional Organization
Primitive types
| Type | Description | XSD datatype | Value examples |
|---|---|---|---|
string | Text | xsd:string | "hello", "" |
int | Integer | xsd:integer | 0, 42, -7 |
float | Floating-point | xsd:float | 3.14, -0.5 |
boolean | Boolean | xsd:boolean | true, false |
An int value is typed xsd:integer (unbounded), never xsd:int. See
Turtle correspondence.
Temporal types
Temporal types carry dates, times, timestamps and durations. Their values are written as smart literals, a type keyword plus human-friendly notation in parentheses (see Temporal values). Dolfin parses the notation and emits the exact XSD lexical form.
| Type | XSD datatype | Smart literal | Emitted value |
|---|---|---|---|
date | xsd:date | date(June 1st 2026) | 2026-06-01 |
time | xsd:time | time(2:30 PM UTC) | 14:30:00+00:00 |
date_time | xsd:dateTime | date_time(June 1st 2026, 14:30) | 2026-06-01T14:30:00 |
duration | xsd:duration | duration(1y 6mo) | P1Y6M |
duration is also the time dimension of the quantity types below:
duration(20 min) and quantity(20 min) are the same value,
"PT20M"^^xsd:dayTimeDuration.
Two file-level directives supply defaults used while resolving temporal values:
@locale d/m/y: field order for numeric dates without an inlineasmask.@timezone <zone>: default timezone fortime/date_timevalues without an inline offset.
See File-level directives. Inline
information (an as mask, an inline offset) always overrides the directive.
Quantity types
A physical dimension is a built-in lowercase type, used like float or
date. Its values are quantity(...) literals of that
dimension, in any unit of it:
concept Dog:
has weight: mass
has topSpeed: optional speed
has length: optional length # a field may share a type's name
fact rex a Dog
weight quantity(45 kg)
topSpeed quantity(42 km/h)
length quantity(1.2 m)
| Type | RDF range | Value example | Emitted value |
|---|---|---|---|
mass | quantitykind:Mass | quantity(45 kg) | "45"^^<http://qudt.org/vocab/unit/KiloGM> |
length | quantitykind:Length | quantity(1.2 m) | "1.2"^^<http://qudt.org/vocab/unit/M> |
speed | quantitykind:Velocity | quantity(42 km/h) | "42"^^<http://qudt.org/vocab/unit/KiloM-PER-HR> |
duration | xsd:duration | quantity(20 min) | "PT20M"^^xsd:dayTimeDuration |
The other quantity types are current, temperature, amount,
luminosity, acceleration, force, pressure, energy, power,
frequency, area, volume, density and momentum; the
Units & Quantities reference lists
each one with its dimension and QUDT quantity kind. A value of another
dimension, or a bare number, is error S008. The names are not keywords:
length or power remain valid field names. Older models that declare
concept Mass and use Mass or unit.Mass as a range still compile, with
lint warnings and quick-fixes towards mass (see
legacy names).
Concept types
Any concept name is a type. Using it as a range means “an instance of that
concept” (or a subtype of it, since sub inheritance is transitive):
concept Animal
concept Dog:
sub Animal
concept Shelter:
has residents: some Animal # Dogs qualify, being Animals
Concept-typed values are references (:id) or inline anonymous blocks
([ ... ]) in facts, see Facts.
Class expressions: or, and, not
Concept types combine with or (either), and (both) and not (anything
but):
concept Clinic:
has patient: (Dog or Cat)
has staff: (Person and not Intern)
has partner: (Hospital or (Clinic and not Franchise))
property treats: (Vet or Nurse) -> not Human
- Every
or/andgroup is written in parentheses:(A or B),(A and B and C). A single name needs none. - Groups nest:
(X or (Y and not T)). andandorcannot be mixed in one group:(A or B and C)is an error, write(A or (B and C))or((A or B) and C).notapplies to what directly follows it:not T, or a whole group withnot (A or B). It needs no parentheses, also at the top level (has partner: not Competitor).oraccepts primitives ((string or int));andandnotwork on concepts only.or,andandnotare reserved words: a concept or field namedandis written`and`.
See Turtle correspondence for the OWL form.
Restrictions: with
with p: C is a type made of the things whose property p points to a C:
an anonymous owl:Restriction. It declares nothing: p must already be a
property (a property declaration or a has field), and a name that is
unknown or is a concept is an error.
has and with are different verbs:
| Verb | Meaning | Default count |
|---|---|---|
has p: C (concept body) | declares the field p, with a global range | 0..* (any) |
with p: C (type expression) | a local constraint on an anonymous class | at least one |
A bare with p: C means “at least one p that is a C”: a restriction that
required nothing would be useless. The forms are:
| Dolfin | Meaning |
|---|---|
with p: C | at least one value of p is a C |
with p: only C | every value of p is a C |
with p: 2 C | exactly 2 values of p are Cs |
with p: at least 2 C, with p: at most 3 C, with p: 1..4 C | a bounded number of C values |
with p: = alice, with p: = "Bob" | p has this exact value |
with ^p: C | the inverse of p: something that is a C points to this one through p |
The counts are the cardinality keywords of has.
only stays an ordinary identifier everywhere else.
Inline form. T with p: C is (T and (with p: C)), usable wherever a type
is. Several withs chain, each one a member of the same and:
concept Person:
has hasChild: Person
property knows: Person -> Person
property admires: (Person with hasChild: Person) -> Person
property trusts: Person with hasChild: Person with knows: only Person -> Person
A filler is a single name or a parenthesised group, so a chained with
never attaches to the previous filler. Narrow a filler inline with
parentheses: with hasChild: (Person with knows: Person).
Block form. Under sub T, equivalent to T, or a property’s domain T /
range T line, an indented block of with lines is the
intersection of T with every line. A block under a with line narrows that
line’s filler:
concept Process
concept TemporalRegion
concept TemporalInterval
property occupiesTemporalRegion: Process -> TemporalRegion
property occurrentPartOf: Process -> Process
concept Act:
sub Process
with occupiesTemporalRegion: TemporalRegion # Process and ∃occ.(TemporalRegion and …)
with occurrentPartOf: TemporalInterval
with ^occurrentPartOf: Process
withapplies to concepts only:int with p: Candwith age: intare errors.withis a reserved word: a concept or field namedwithis written`with`.
See Turtle correspondence for the OWL form.
Defined concepts: equivalent to
sub X says every member of the concept is an X (necessary condition).
equivalent to X says the concept is X: the members are exactly the
things that are Xs (necessary and sufficient, owl:equivalentClass).
X is any type expression, and takes the same with block as sub:
concept Person:
has hasChild: Person
# Parent ≡ Person ⊓ ∃hasChild.Person
concept Parent:
equivalent to Person
with hasChild: Person
# Mother ⊑ Person ⊓ ∃hasChild.Person (necessary only)
concept Mother:
sub Person
with hasChild: Person
- A concept may have several
equivalent tolines, next tosubandhaslines. Each line, and each type inequivalent to A, B, gives its ownowl:equivalentClassstatement, so the concept is equivalent to all of them. Awithblock needs a single type on its line. - There is no
=form:equivalent tois the only way to say it. - Names in the line are checked like
subparents: an undeclared name is an error.
Property domain and range lines
A class expression too long for property p: D -> R goes in a domain or
range body line, with the same block form:
concept Process
concept TemporalRegion
concept TemporalInterval
property occupiesTemporalRegion
property occurrentPartOf
property partOfProcess
sub occurrentPartOf
domain Process
with occupiesTemporalRegion: TemporalRegion
with occurrentPartOf: TemporalInterval
range Process
with ^occupiesTemporalRegion: TemporalRegion
Each side is given once, either in the one-line form or in one line (error S014 otherwise). See Properties and Turtle correspondence.
Closed concepts (one of)
A closed concept enumerates a fixed set of named members; only those members are valid values:
concept Status:
one of:
Pending
Active
Archived
concept Ticket:
has status: one Status
Members are referenced unquoted (Active), not as strings.
Cardinality
Cardinality is orthogonal to type: it constrains how many values of the type
an attribute may hold (one, optional, some, N, N..M, …). It is written
before the type. See Cardinality for the full table.
has tags: some string # one or more strings
has born: at most 1 date # zero or one date
has visits: date_time # any number of timestamps