Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Sometimes, in describing the syntax, EBNF-like notation is used, with the following meaning:
To keep this chapter as readable as possible, we have chosen to omit some details that are irrelevant for practically all Ampersand modelers. In the very rare case that these technicalities are of interest, the reader could have a look in the sourcecode of the parser, where all EBNF statements are fully detailed in comments.
Operator
meaning
<foo>?
Zero or one occurrence of <foo>
<foo>+
One or more occurrences of <foo>
<foo>*
Zero or more occurrences of <foo>
A concept statement defines a concept in natural language. A concept is a name for similar things. For example: Peter
, John
, and Barack
are things you might want to call Person
, whereas 45-NP-88
and KD-686-D
could be instances of the concept LicensePlate
.
This statement means that there exists a concept called <upper case identifier>
in the current context.
<upper case identifier>
specifies the name of the concept. It starts with an upper case character and may subsequently have any combination of upper case (ABCDEFGHIJKLMNOPQRSTUVWXYZ), lower case (abcdefghijklmnopqrstuvwxyz), digits (0123456789) and underscore (_).
<String>
defines the concept. Please describe in natural language the conditions that make an atom belong to this concept. Your definition is used by the documentation generator, which expects it to be a grammatically correct and complete sentence.
The name of a concept starts with an uppercase character.
A concept should be used for immutable concepts. E.g. use a concept Person
to express that a person will always be a person and will not change in, let us say, a table. However, don't use Employee
, because termination of an employee's contract causes a person to be an employee no longer. So employees are not immutable. To be an employee is a dynamic property, so model it as a relation.
The description will be printed in the functional specification, so please check that your definition is a complete sentence.
Concepts need not be defined. If you use a concept without a definition, Ampersand makes it for you.
For the purpose of documentation, you may state the language in which the meaning is written. You may also state in which markup you have written your meaning. Examples:
If you specify the language, Ampersand can restrict the documentation for the language you choose. Currently, you can only choose DUTCH
or ENGLISH
. The default language is English
By specifying a markup language, Ampersand interprets the text as specified. If you do not specify the markup language, your text is interpreted as restructured text. The available markup languages are LATEX
, MARKDOWN
, HTML
, and REST
. The default markup language is REStructured Text (REST).
An information system should represent the truth. So, as a designer you must know a thing or two about truth.
Let us introduce some language to talk about truth. Consider a fact "Joe Smith lives in New York." from an Ampersand perspective. In Ampersand, we can analyse this as follows:
Let Person
and City
be ****
Let "Joe Smith"
be an of the concept Person
and "New York"
an atom of the concept City
.
Let us use the livesIn[Person*City]
to contain our fact.
livesIn
is the relation name and [Person*City]
is the signature of this relation.
Person
is the source of this relation and City
is the target.
If the pair ("Joe Smith","New York")
is an element of this relation, Ampersand considers the statement "Joe Smith" livesIn "New York"
to be true. So all pairs in a relation represent facts, i.e. true statements.
Ampersand takes a pragmatic stance on truth: You model only things that make sense to the business. This illustrates the distinction between sensible and senseless statements. A sensible statement (we say: "It makes sense.") is a statement that can be true or false. Sentences that are not sensible (we can say: it is non-sense) are to be avoided. The Ampersand type system helps you to make sensible statements only.
Truth always has context. If we say "Jack was married to Jackie", this statement is true in a where "Jack" refers to the 35th president of the United States, John F. Kennedy. However, this statement is not true in a context where there is no Jack. And in a context where marriage doesn't exist, this statement makes no sense.
This chapter describes the full language Ampersand. Please use it as a reference rather than an introductory course.
Watch to learn how we use the words atom, concept, and relation. Below is a list of other words with a specific meaning in Ampersand.
Syntactic definitions are given where the underlying notions (e.g. rule, relation, pattern, etc.) are discussed. The metasyntax is singled out . Because are defined in relation algebra, their semantics are explained in various ways to suit the background of each individual reader. Terms are the only algebraically defined things.
This section is organized by discussing each notion in isolation. Hyperlinks are added in the text to let the reader navigate on her own. The text is suitable for reference purposes, so there is no preferred order in reading.
Most things in your model are in it for a reason. To document these, you should use the PURPOSE statement.
PURPOSE
<type of thing>
<name>
<language>?
<markup>?
{+
<anything>
+}
Where <type of thing>
and <name>
are the type and name of the thing that is refered to. This could be one of: CONCEPT
, RELATION
, RULE
, IDENT
, VIEW
, PATTERN
, INTERFACE
, CONTEXT
The optional and can be used to override the settings for language and markup. If omitted, these are inherited from the pattern of context where the PURPOSE statement is specified in.
Examples:
When defining the purpose of a relation, make sure that Ampersand can identify the relation unambiguously. If you have multiple relations accountOwner
, add the signature to disambiguate it. For instance:
For the purpose of documentation, you may state the language in which you write a purpose. You may also state in which markup language you use. Examples:
If you specify the language, Ampersand can restrict the documentation for the language you choose. Currently, you can only choose DUTCH
or ENGLISH
. The default language is English.
By specifying a markup language, Ampersand interprets the text as specified. If you do not specify the markup language, your text is interpreted as REStructured Text (REST
). The available markup languages are LATEX
, MARKDOWN
, HTML
, and REST
.
MEANING can be used with CONCEPT-statements, RELATION-statements, and RULE-statements, to define the meaning of your concepts, relations, and rules.
A meaning is optional and is characterized by the reserved word MEANING
. It specifies the meaning of a concept, a relation, or a rule in natural language. The meaning is used to generate documentation and is printed in the functional specification. A <meaning>
can be any text, starting with {+
and ending with +}
e.g.
The optional <language>
is specified as
IN ENGLISH
or
IN DUTCH
.
Example :
This is a way to override the default language (which is English).
Sometimes you need formatting in the meaning, such as dotted lists, italics, or mathematical symbols. For this purpose you have a choice in which syntax you specify the meaning. The optional <markup>
is one of :
REST
(Restructured text. This is the default)
HTML
LATEX
MARKDOWN
Example :
Ampersand uses Pandoc to offer a choice for your markup. See pandoc.org for details.
Word | Meaning | Example | Purpose |
an indivisible item |
| to represent a thing |
a name to categorize similar items |
|
Pair | two atoms: a source and a target atom |
| to state that two atoms are related |
a set of pairs that is identifyable in a context by its name and type |
| to build true statements and store pairs persistently in an application |
a constraint, which is supposed to remain satisfied. |
| to provide meaning in a given context |
satisfy | A rule is satisfied (in a context) if the data (in that context) do not cause any violation of that rule. | to calculate violations at run-time helps users do the right things |
a set of rules |
| to gather rules that belong together for reusing them in different contexts |
Population | a set of pairs in a context |
| to represent the facts (i.e. true statements) in an information system |
a population together with a set of rules that are satisfied by the population. |
| to maintain a consistent representation of a real life situation |
View | A set of pairs that can be shown to users in a particular formulation. | to represent facts |
A structure meant for "the outside world" to communicate with the system and possibly change the population. |
| to let "the outside world" communicate with the system in a given context and possibly change its population |
Multiplicity | A predefined property of a relation |
| to constrain a relation with predefined properties |
A combination of relations and operators that satisfy the Ampersand syntax |
| to express rules |
Operator | a symbol used in combining terms into other terms. |
| to express more complex rules. |
A rule that defines specialization between two (or more) concepts. |
| To specify a building block for a classification hierarchy. |
Role | A name for a group of people |
| to talk about users without having any users |
To represent a real-world thing in an information system context, you use atoms.
An atom refers to an individual object in the real world, such as the student called "Caroline". But what if there are three different Carolines? What does it mean to say: "Caroline has passed the exam for Spanish Medieval Literature."? This sentence might be true for one Caroline, but false for the others. Clearly, to avoid ambiguous sentences, an atom must identify exactly one real-world object, no more, no less. Or rather, it suffices that the atom identifies one object within the context in which we are working: if the context is a group with only one Caroline, there will be no ambiguity. Similarly, ABBA is unique among all pop groups in the world; there ought to be only one building permit with number 5678; etcetera.
"Caroline"
, 5
, 1917-11-07
48
, 10.34
, 2.
, .001
, -125
, +5.33333
, 2.5E2
, 5E-3
The syntax of atoms is largely taken from ISO8601 and corresponds to the syntax of SQL and Excel. (Acknowledgement: the following text was adapted from Wikipedia)
Date and time values are ordered from the largest to smallest unit of time: year, month (or week), day, hour, minute, second, and fraction of second. The lexicographical order of the representation thus corresponds to chronological order, except for date representations involving negative years. This allows dates to be naturally sorting|sorted by, for example, file systems.
Each date and time value has a fixed number of digits that must be padded with leading zeros.
Representations can be done in one of two formats - a basic format with a minimal number of separators or an extended format with separators added to enhance human readability. The separator used between date values (year, month, week, and day) is the hyphen, while the colon is used as the separator between time values (hours, minutes, and seconds).
For reduced accuracy, any number of values may be dropped from any of the date and time representations, but in the order from the least to the most significant. For example, "2004-05" is a valid ISO 8601 date, which indicates May (the fifth month) 2004. This format will never represent the 5th day of an unspecified month in 2004, nor will it represent a time-span extending from 2004 into 2005.
If necessary for a particular application, the standard supports the addition of a decimal fraction to the smallest time value in the representation.
Atoms are represented in an SQL database. For this purpose, every atom has a type (sometimes called the technical type). The representation in SQL is given in the following table.
The last column, eq, tells whether Ampersand implements equality on these types. If equality is not defined, the operators \/
, /\
, -
, \
, /
, ;
, and <>
cannot be used.
The distinction between closed and open types is relevant in the following situations:
The complement of a relation, -r[A*B]
, is defined only if both A
and B
are closed.
The full relation, V[A*B]
is defined only if both A
and B
are closed.
A service INTERFACE X : e
requires that the target of e
is closed.
Violations are currently signaled at runtime, but future versions of Ampersand will signal these violations at compile time.
Every atom whose atomic type is marked "yes" in the column "eq" can be compared for equality. For all other atoms, equality is not defined.
The following Ampersand statement declares the atomic type of a concept:
e.g.
If Person
and Company
are both LegalEntity
, then both of them will be implicitly declared as ALPHANUMERIC
too.
A relation statement says that a relation exists. It introduces (defines, declares) the relation in the context that uses the relation statement.
A population statement specifies which pairs (of atoms) are in a relation.
A relation is a set that contains pairs of atoms. Over time, pairs can be inserted into or deleted from a relation, for example by a user typing data into an Ampersand application. So the content of a relation is changing over time.
When discussing relations, an arbitrary relation is referred to as , , or . To say that a pair belongs to a relation , we write or alternatively .
In this example:
contract
is the name of the relation,
Order
is the source concept of the relation,
ContractID
is the target concept of this relation, and
UNI
and TOT
are constraints of this relation.
Each relation used in Ampersand has to be declared. This means that the developer tells the system that this particular relation exists. A relation declaration can have one of the following formats:
In the declaration RELATION owner[Person*Building]
, owner
is the name and [Person*Building]
is the type of the relation. Relation names start with a lower case character, to avoid confusion with concept names. The signature of this relation is owner[Person*Building]
. The signature identifies the relation within its context. The left hand concept, Person
, is called the source of the relation and the right concept, Building
, is called the target.
All three formats define a relation by its name, its source concept and its target concept. By convention, the name of a relation is a single word that starts with a lower case letter. The source and target concepts start with an upper case letter. This convention avoids confusion between concepts and relations.
A relation statement means that there exists a relation in the current context with the specified name, source concept and target concept.
A relation statement may occur anywhere inside a context, both inside and outside a pattern.
The optional <properties>
and <pragma>
-parts are discussed in the sequel. The <meaning>
-part is discussed here.
The name, source concept and target concept together identify a relation uniquely within its context. As a consequence, the name of a relation does not have to be unique. E.g. name[Book*Name]
can be specified in the same context as name[Person*Name]
. Because they have different source concepts, these are different relations.
The <properties>
-part is meant for writing multiplicity constraints in a comma separated list between square brackets '[' and ']'. E.g. [UNI,TOT]
. The following properties can be specified on any relation r[A*B]
There are additional relations that can be specified on endo relations. An endo relation is a relation where the source and target concepts are equal. r[A*A]
.
Let's assume that we want to express that any person can live in one city only. So under this constraint "Joe Smith lives in New York" and "Joe Smith lives in Denver" cannot both be true at the same time.
In relation algebra, we say that the relation is univalent, which means that every atom in the source concept can only be paired with a single atom in the target concept. This is modeled as
A pragma is optional and is characterized by the reserved word PRAGMA
. The PRAGMA
is followed by two or three strings. It is used to construct sentences in natural language, using pairs from the actual population of a relation. A pragma specifies how we speak (in natural language) about any pair in the relation. Ampersand also uses pragmas to generate examples in the functional specification. Example of a pragma with three strings:
To use this pragma on the pair (John,Amsterdam)
results in the sentence "Student John flies the flag of Amsterdam in top."
. The two atoms are fitted in between the three strings. A pragma with two strings is identical to a pragma in which the third string is empty.
(The PRAGMA
keyword will become obsolete in a future version of Ampersand. It will be replaced by the VIEW
-statement which offers more flexibility in composing sentences.)
Example:
The PRAGMA
tells us that it makes sense to utter the phrase "Provider Mario's Pizza's has accepted order 12345."
For a full discussion of meaning, we refer to this page
.
A classify statement is also called a specialization. It specifies that atoms of one concept are atoms of another concept as well. You can use it to buils classifications like Linnaeus did.
In a specialization, e.g. CLASSIFY Sedan ISA Car
, we call the first concept (Sedan
) the specific concept and the second (Car
) the generic concept. The meaning of a specialization is that every atom from the specific concept is an atom from the generic concept as well. So every (atom that is a) Sedan is a Car as well.
So in general: CLASSIFY
ISA
means: .
To save some writing, you may specify
This means exactly the same as
A specialization is a static relationship. If you want to say that a student is a person, please consider whether you want this to be static. If a person can enroll to become a student, or graduate or drop out to become non-student again, the dynamics of that cannot be captured in a specialization. Use a relationship instead to model the state of being a student.
E.g. RELATION student[Person*Enrollment]
By adding and removing pairs to that relation, it continuously reflects which persons are a student.
The purpose of a rule is to constrain data. Refer to the in the tutorial for examples and a practice-oriented explanation.
A rule statement defines something that should be true. It does not define the enforcement.
A <rule>
has the following syntax:
Terms and operators are discussed in :
A <label>
is optional. It can be a single word or a string (enclosed by double brackets) followed by a colon (:
).
The meaning of a rule can be written in natural language in the Meaning part of the RULE statement. It is a good habit to specify the meaning! The meaning will be printed in the functional specification. The meaning is optional.
The <text>
part is where the the meaning is written down. It is enclosed by {+
and +}
and may be spread across multiple lines. If you need specific markup, turn to for a full explanation.
Messages may be defined to give feedback whenever the rule is violated. The message is a string. When you run your prototype this is printed in a red box when the rule is violated. You will see the violations by clicking on that message.
A violation message can be constructed so that it gives specific information about the violating atoms:
Every segment must be of one of the following forms:
TXT
String
SRC
Expression
TGT
Expression
A rule is violated by a pair of atoms (source, target). The source atom is the root of the violation message. In the message, the target atoms are printed. With the Identity relation, the root atom itself can be printed. You can use an expression to print other atoms. Below two examples reporting a violation of the rule that each project must have a project leader. The first prints the project's ID, the second the project's name using the relation projectName:
VIOLATION ( TXT "Project ", SRC I, TXT " does not have a projectleader")
VIOLATION ( TXT "Project ", SRC projectName, TXT " does not have a projectleader")
By default, rules are invariant rules. By preceding the rule statement with a role specification for this rule, the rule becomes a process rule.
tbd
&
property
semantics
UNI
univalent
For any a
in A
there can be not more than one b
in B
in the population of r
. This implies that every a
occurs not more than once (is unique) in the source of r
.
INJ
injective
For any b
in B
there can be not more than one a
in A
in the population of r
. So, every b
occurs not more than once in the target of r
.
SUR
surjective
For any b
in B
there must be at least one a
in A
in the population of r
.
TOT
total
For any a
in A
there must be at least one b
in B
in the population of r
.
&
property
semantics
SYM
symmetric
For each (a
,b
) in r
, (b
,a
) is in r
.
ASY
antisymmetric
If (a
,b
) and (b
,a
) are both in r
, then a
= b
TRN
transitive
If (a
,b
) and (b
,c
) are both in r
, then (a
,c
) is in r
.
RFX
reflexive
For each a
in A
, the pair (a
,a
) is in the population of r
IRF
irreflexive
For each a
in A
, the pair (a
,a
) is not in the population of r
PROP
-
shortcut for the combination of symmetric and antisymmetric.
type
purpose
SQL
closed
eq
ALPHANUMERIC
to represent strings of short length, i.e. less than 255 characters
VARCHAR(255)
yes
yes
BIGALPHANUMERIC
to represent large strings of limited length, i.e. less than 64 kb
TEXT
no
yes
HUGEALPHANUMERIC
to represent strings of arbitrary length
MEDIUMTEXT
no
no
PASSWORD
to represent passwords in a secure way
VARCHAR(255)
no
yes
BINARY
to represent uninterpreted binary data of short length
BLOB
no
no
BIGBINARY
to represent large binary data of limited length
MEDIUMBLOB
no
no
HUGEBINARY
to represent large binary data of arbitrary length
LONGBLOB
no
no
DATE
to represent dates compatible with ISO8601
DATE
yes
yes
DATETIME
to represent timestamps compatible with ISO8601
DATETIME
yes
yes
BOOLEAN
to represent True and False values
BOOLEAN
yes
yes
INTEGER
to represent positive and negative whole numbers in the range [-2^63..2^63 -1]
BIGINT
yes
yes
FLOAT
to represent floating-point numbers compatible with ISO8601
FLOAT
no
no
Object
to represent a key value for objects; it is not meant to be visible to end-users.
VARCHAR(255)
yes
yes
all other atoms
VARCHAR(255)
yes
yes
When a relation is used in a term, it stands for all pairs it contains at the moment it is evaluated. Those pairs (also referred to as the contents or population of the relation) can change over time as users add or delete pairs from it.
When a relation is used in a term, we can just use its name if that is unambiguous. For instance the name owner
refers to RELATION owner[Person*Building]
if that is the only relation the ampersand-compiler can link it to. In some cases, however the name alone is ambiguous. For example if there are two relations with the same name and different signatures. In such cases Ampersand will try to infer the type from the context. That however does not always succeed. In such cases, Ampersand generates an error message that asks you to remove the ambiguity by adding the correct type.
If a pair is an element of a relation , we write . Alternatively we may write , since we know that is a set.
For every concept , the term exists. It refers to the identity relation. It means that for every and we have:
The type of is . In Ampersand code you write I[C]
.
For every pair of concepts and the term refers to the complete relation. For every and we have:
The type of is . In Ampersand code you write V[A*B]
.
Would you like a different explanation of the primitive terms? This page explains the primitive terms in terms of set theory. Click here for the explanation of primitive terms in natural language.
The notation means that the pair (a,b) is in relation . This page defines when pair (a,b) is in relation (the intersection of and ), (the union of and ), (the difference of and ).
intersection : . In other words: if the pair is both in relation and , then it is in the intersection of and .
union : . In words: if the pair is in the relation or in , then it is in the union of and .
difference : . In other words, the term contains all pairs from that are not in .
The complement (or negation) of a relation is defined by means of the difference operator:
complement : If is defined as , then is the set of all tuples in (the Cartesian product) that are not contained in . So
Note that the complement is defined in terms of and . So, two relations with an identical population yet a different type may have different complements.
This page shows how you can type boolean (and other) operators in your Ampersand script.
Would you like a different explanation of the boolean operators? This page explains the boolean operators in terms of set theory.
This page describes the notion of term. Its subpages provide several interpretations of terms, all of which are valid so you can use each interpretation at your own discretion.
The purpose of a term is to compute pairs that constitute a relation. We use operators to assemble terms from smaller terms, to express in formal language precisely what is meant in the natural language of the business. The smallest term is a single relation.
We noticed that our readers have different backgrounds. They have different preferences about the way we explain the operators in Ampersand. Some prefer an explanation in logic, others in algebra, and still others in set theory. So we decided to explain the operators in many different ways simultaneously, hoping that one of them suits your preference.
A term is a combination of operators and relations. Its meaning is a set of pairs, which is in fact a newly created relation. The word "expression" may be used as a synonym for "term" in the context of Ampersand.
owner
r;s~
I /\ goalkeeper;goalkeeper~
destination;"Algarve" |- spoken;"Portugese"
Every term is built out of relations, which are combined by operators. An term has one of the following 8 syntactic structures
Operators
The operators come in families. We advise novices to study only the rule operators, boolean operators and relational operators. There is a wealth of things you can express with just these operators. The residual operators seem harder to learn and the Kleene operators are not fully implemented yet. You can click the hyperlink to navigate to the semantics of each family.
Operators with different binding power may be used in the same term without brackets, because the binding power tells how it is interpreted. For example, means because has a higher binding power than .
Operators with the same binding power must be used unambiguously. For example: means something different than . In such cases Ampersand insists on the use of brackets, so readers without knowledge of the binding powers of the operators can read a term unambiguously.
Repeated uses of an associative operator does not require brackets. So is allowed because is associative.
When coding in Ampersand, these operators are typed with characters on the keyboard. The following table shows the operators in math and their equivalent in code:
To say things such as "the name of the owner", we want to string together multiple relations (viz. name
and owner
). Relational operators allow us to make such statements.
A relation can be altered by swapping the elements of every pair in the relation. Mathematically, is a different from . This operation is called the converse operator. It produces a new relation from an existing one. It is denoted by writing \smallsmile\ (pronounced 'wok' or ’flip’) after the relation name. This is how converse is defined:
If has type, then r\smallsmile\ has type .
The composition operator is denoted by a semicolon between two terms. It is pronounced as 'composed with'. Let us take a look at composed with . Let and be two relations, with the target of r being the same as the source of s. Then the composition of and is defined by:
If has typeand has type, then has type .
This page shows how you can type boolean (and other) operators in your Ampersand script.
Would you like a different explanation of the relational operators? This page explains the relational operators in terms of set theory. This page explains them in natural language. Click here for some algebraic rules about relational operators.
Semantics tell us about meaning. About how to interpret terms and their operators.
We present the semantics of terms in 5 different (but equivalent) ways: one explanation in terms of logic, one in set theory, one in terms of axioms (algebraically), one in natural language, and one visual explanation. These ways are equivalent, so you can interpret a term in any of the presented ways. Any way will do; take your pick!
(the pages without hyperlinks are yet to be made).
To say things such as "the name of the owner", we want to string together multiple relations (viz. name
and owner
). Relational operators allow us to make such statements.
The meaning of relational operators and is best explained by means of examples.
Assume we have a relation, label[Contract*Colour]
, which contains the colour of labels on contracts. A fact "1834" label "blue"
means that contract 1834 has a blue label.
Also assume another relation stored[Contract*Location]
, which gives the location where a contract is stored. Fact "1834" store "cabinet 42"
means that contract 1834 is stored in cabinet 42.
A relation can be altered by swapping the elements of every pair in the relation. Mathematically, is a different from . In natural language, however, the meaning does not change. So if"1834" label "blue"
means that contract 1834 has a blue label, "blue" label~ "1834"
also means that contract 1834 has a blue label.
The sentence: "All contracts with a blue label are stored in cabinet 42." is represented as "blue" (label\stored) "cabinet 42"
. Literally it says: For every contract, if it has a blue label, then it is stored in cabinet 42.
The sentence "A contract with a blue label is stored in cabinet 42." can be represented as "blue" (label~;stored) "cabinet 42"
. Literally it says: There is a contract that has a blue label and is stored in cabinet 42.
The natural language translation for b r~ a
is the same as language translation for a r b
.
When a is used in a term, it stands for a set of facts that are assumed true on the current time in the current context. Those facts (also referred to as the contents or the population of the relation) can change over time as users add or delete facts from it.
When a relation is used in a term, we can simply use its name if that is unambiguous. For instance the name owner
refers to RELATION owner[Person*Building]
if that is the only relation the ampersand-compiler can link it to. In some cases, however the name alone is ambiguous. For example if there are two relations with the same name and different signatures. In such cases Ampersand will try to infer the type from the context. That however does not always succeed. In such cases, Ampersand generates an error message that asks you to remove the ambiguity by adding the correct type.
If a pair is an element of a relation , we write to denote the fact. It means that we consider to be true (within the current context).
Every atom in a concept identifies itself. If for example concept "Person" contains atoms {"Ann", "Bob", "Cecil"}, "Ann" identifies "Ann", "Bob" identifies "Bob", and "Cecil" identifies "Cecil". This makes "Ann" and "Bob" different atoms (unequal).
Would you like a different explanation of the primitive terms? explains the primitive terms in logic. for the explanation in set theory.
are used when "" is involved.
right residual : . In other words: is in the right residual of and means that for every , pair is in relation implies that pair is in .
left residual : . In words: is in the left residual of and
means that for every pair is in relation implies that pair is in .
diamond: . In words: For every , both and are true or both are false.
shows how you can type boolean (and other) operators in your Ampersand script.
Would you like a different explanation of the residual operators? explains them in natural language. for visualized examples about residual operators.
To say things such as pair ("peter","macbook")
is either in relation ownsa
or wantsa
, requires us to use boolean operators , , and .
Let us explain the meaning of relational operators , , and by means of examples.
Assume we have a relation, ownsa[Person*LaptopType]
, which contains the persons who own a particular type of laptop. A fact "peter" ownsa "macbook"
means that Peter owns a MacBook.
Also assume another relation wantsa[Person*LaptopType]
, which contains the persons who want a particular type of laptop. A fact "peter" wantsa "macbook"
means that Peter wants a MacBook.
The sentence: "Peter owns a MacBook or Peter wants a MacBook." is represented as
"peter"
(ownsa
wantsa
) "macbook"
.
The sentence: "Peter owns a MacBook and Peter wants a MacBook." is represented as
"peter"
(label
colour
) "macbook"
.
The sentence: "Peter owns a MacBook and Peter does not want a MacBook." is represented as
"peter"
(label
colour
) "macbook"
.
Family | binary operators | binding power | unary operators | binding power |
---|---|---|---|---|
operator name | code | math | remark |
---|---|---|---|
There is a pattern to this. A computer can generate a literal translation from the formula to natural language. However, that translation looks clumsy, verbose and elaborate. It is up to you to turn that in normal language. For examples . The systematic translation is given in the following table:
Would you like a different explanation of the relational operators? explains the relational operators in terms of set theory. An explanation in logic is given . for some algebraic rules about relational operators.
There is a pattern to this. A computer can generate a literal translation from the formula to natural language. However, that translation looks clumsy, verbose and elaborate. It is up to you to turn that in normal language. For examples . The systematic translation is given in the following table:
Would you like a different explanation of the relational operators? explains the boolean operators in terms of set theory. An explanation in logic is given . for some algebraic rules about boolean operators. If you want to see it explained visually in Venn-diagrams, .
rules
1 (weakest)
2
prefix
4
postfix
3
Kleene
postfix
equivalence (equal)
=
use only in a rule
inclusion
|-
use only in a rule
intersect
/\
associative, commutative, idempotent
union
\/
associative, commutative, idempotent
difference (minus)
-
complement
-
in code: Prefix; in math: Overline
compose
;
associative
converse (flip)
~
postfix
left residual
/
right residual
\
diamond
<>
relational product
!
associative
cartesian product
#
deprecated
reflexive transitive closure
*
in code: not implemented; in math: Postfix
transitive closure
+
in code: not implemented; in math: Postfix
Category
Logic
Sets
Axioms
Natural Language
Visual
primitive terms
axioms
visual
boolean
relational
residual
sets
axioms
products
logic
sets
axioms
natural language
visual
This chapter discusses the and the in the following sections.
The meaning of residual operators , , and is best explained by means of examples.
Assume we have a relation, label[Contract*Colour]
, which contains the colour of labels on contracts. A fact "1834" label "blue"
means that contract 1834 has a blue label.
Also assume another relation stored[Contract*Location]
, which gives the location where a contract is stored. Fact "1834" store "cabinet 42"
means that contract 1834 is stored in cabinet 42.
The sentence: "All contracts with a blue label are stored in cabinet 42." is represented as "blue" (label\stored) "cabinet 42"
. Literally it says: For every contract, if it has a blue label, then it is stored in cabinet 42.
The sentence: "All contracts that are stored in cabinet 42 have a blue label." is represented as "blue" (label~/stored~) "cabinet 42"
. Literally it says: For every contract, if it is stored in cabinet 42, then it has a blue label.
The sentence: "All blue labeled contracts and no others are stored in cabinet 42." is represented as "blue" (label~<>stored) "cabinet 42"
. Literally it says: For every contract, if it has a blue label, then it is stored in cabinet 42 and if it is stored in cabinet 42, then it has a blue label.
There is a pattern to this. A computer can generate a literal translation from the formula to natural language. However, that translation looks clumsy, verbose and elaborate. It is up to you to turn that in normal language. For examples . The systematic translation is given in the following table:
Would you like a different explanation of the residual operators? explains them in logic. for visualized examples about residual operators.
A relation is by definition a subset of the Cartesian Product of the source and target sets. So, if two different relations r and s are defined on the same source A and target B, then the ordinary set operators can be applied to produce a new relation.
intersection : is the set that contains the elements that are contained in relation as well as in , or
union : is the set that contains all elements that are contained either in relation or in , or
difference : is the set that contains the elements of relation that are not contained in , or
The complement (or negation) of a relation is defined by means of the difference operator:
complement : If is defined as , then is the set of all tuples in (the Cartesian product) that are not contained in . So
Note that the complement is defined in terms of and . So, two relations with the identical population yet a different type may have different complements.
shows how you can write these things in your Ampersand script.
Would you like a different explanation of the boolean operators? explains the boolean operators in logic.
When a is used in a term, it stands for the set of pairs it contains at the moment it is evaluated. That set (also referred to as the contents of the relation) can change over time as users add or delete pairs from it.
When a relation is used in a term, we can simply use its name if that is unambiguous. For instance the name owner
refers to RELATION owner[Person*Building]
if that is the only relation the ampersand-compiler can link it to. In some cases, however the name alone is ambiguous. For example if there are two relations with the same name and different signatures. In such cases Ampersand will try to infer the type from the context. That however does not always succeed. In such cases, Ampersand generates an error message that asks you to remove the ambiguity by adding the correct type.
If a pair is an element of a relation , we write . Alternatively we may write .
For every concept , the term represents the identity relation. It is defined by:
The type of is . In Ampersand code you write I[C]
.
For every pair of concepts and the term represents the complete relation. It is defined by:
To say things such as "the name of the owner", we want to string together multiple relations (viz. name
and owner
). Relational operators allow us to make such statements.
A relation that contains pairs of the form can be altered by swapping the elements of every pair in the relation. Mathematically, is a different from . This operation is called the converse operator. It produces a new relation from an existing one. It is denoted by writing (pronounced 'wok' or ’flip’) after the relation name. This is how converse is defined:
If has type , then has type .
The composition operator is denoted by a semicolon ; between two terms. It is pronounced as 'composed with', in this case: composed with .
The composition operation is defined as follows: Let and be two relations, with the target of r being the same as the source of s. Then the composition of and , is a relation with signature
Would you like a different explanation of the relational operators? explains the relational operators in logic. explains them in natural language. for some algebraic rules about relational operators.
and
, , and
, , and
, , and
and
The type of is . In Ampersand code you write V[A*B]
.
Would you like a different explanation of the primitive terms? explains the primitive terms in logic. for the explanation of primitive terms in natural language.
Formally
Natural language template
a (r;s) b
There exists an x : if a r x
then x s b
.
b r~ a
a r b
.
Formally
Natural language template
a r b
or a s b
.
a r b
and a s b
.
a r b
and nota s b
.
Formally
Natural language template
a (r\s) b
For every x
: if x r a
then x s b
.
a (r/s) b
For every x
: if b s x
then a r x
.
a (r<>s) b
For every x
: if a r x
then x s b
and if x s b
then a r x
.
For a visual presentation of the semantics of terms, we use Venn-diagrams.
The boolean operators of Ampersand behave as one would expect in any boolean algebra. Union () and intersection () are both idempotent, commutative, and associative operators. In Ampersand we use a binary difference operator over with the usual semantics: . The (more customary) complement operator is a partial function, because Ampersand supports heterogeneous relation algebra.
The operator (union) satisfies the following axioms:
(commutativity of )
(associativity of )
(idempotence of )
The difference is the smallest relation that satisfies . Smallest means: If there is a for which , this implies that .
The intersection is defined as:
The complement operator is defined as . The type comes from the term(s) in which is embedded. If that type does not exist or if it is ambiguous, Ampersand will refuse to compile with an appropriate error message.
This page shows how you can write these things in your Ampersand script.
To say things such as "the name of the owner", we want to string together multiple relations (viz. name
and owner
). Relational operators allow us to make such statements.
There are two relational operators: the converse () and the composition (semicolon ). This page discusses the most important laws about these operators.
There are two things you should know about the converse operator. The first is that the converse of the converse gives you the relation itself, whatever that relation may be:
The second thing you should know is that arguments switch places if the converse is brought outside (or inside) brackets
The composition operator is denoted by a semicolon (;) between two terms. It is pronounced as 'composed with', in this case: composed with .
Composition is associative, which means:
The meaning stays the same, no matter how you place the brackets. So Ampersand lets you omit brackets entirely. You may write instead of or .
Composition has a left and a right identity. Let be a relation, then
This page shows how you can write these things in your Ampersand script.
Would you like a different explanation of the semantics of the relational operators? Click here for an explanation in sets. This page explains them in natural language. This page explains the relational operators in logic.
Consider two relations: traveler[Trip*Person]
and dest[Trip*Destination]
. The first relation tells which persons have traveled on which trip. The diagram shows this as red dashed lines. The second relation links trips to destinations. It is depicted by dotted blue lines in the diagram.
Each pair (fact) in the diagram can be written as a fact in two ways, using the converse operator:
From the diagram, we assume that each pair represents a true statement (i.e. a fact). The statements are given both formally and in natural language. The elaborate version is a literate translation of the semantics in logic. The ordinary version tells the same in a more human sounding manner.
Consider two relations: traveler[Trip*Person]
and dest[Trip*Destination]
. The first relation tells which persons have traveled on which trip. The diagram shows this as red dashed lines. The second relation links trips to destinations. It is depicted by dotted blue lines in the diagram.
From this diagram, we can tell which statements are true (i.e. facts). The statements are given both formally and in natural language. The elaborate version is a literate translation of the semantics in logic. The ordinary version tells the same in a more human sounding manner.
The following statements do NOT follow from the population shown in the diagram:
Would you like a different explanation of the residual operators? This page explains them in natural language. Click here for the explanation of residual operators in logic.
Consider two relations: authorized[Account*Person]
and beneficiary[Account*Person]
. The first relation tells which persons are authorized to which accounts. The diagram shows this as red dashed lines. The second relation tells which persons stand to benefit from which accounts.. It is depicted by dotted blue lines in the diagram.
This diagram gives an example population of the relations authorized[Account*Person]
and beneficiary[Account*Person]
. Bob is authorized for account DE9382991 and Ann is authorized for account RS746620. Carl stands to benefit from account NL19RABO03992844 and Ann stands to benefit from account RS746620. Formally, we say:
By combining the relations authorized
and beneficiary
, we can derive the following true statements.
A different way to state the same is:
Would you like a different explanation of the boolean operators? This page explains them in set theory. Click here for the semantics of the boolean operators in algebra. Here you get their definitions in logic.
Formal statement
Elaborate natural language
Ordinary natural language
"Peter" (traveler~;dest) "Rome"
There is a trip that Peter has made, which has Rome as destination.
Peter has made a trip to Rome.
"Peter" (traveler~;dest) "Paris"
There is a trip that Peter has made, which has Paris as destination.
Peter has made a trip to Paris
Formal statement
Elaborate natural language
Ordinary natural language
"Peter" (traveler\dest) "Rome"
For each trip, if Peter has made the trip then its destination is Rome.
Every trip that Peter made has Rome as destination.
"Peter" (traveler\dest) "Paris"
For each trip, if Peter has made the trip then its destination is Paris.
Every trip that Peter made has
Paris as destination.
"Peter" (traveler~/dest~) "Paris"
For each trip, if Paris is the destination then Peter has made that trip.
Every trip to Paris has been made by Peter.
statement
natural language
"RS746620" (authorized/\beneficiary) "Ann"
Ann is authorized for and stands to benefit from for account RS746620.
"NL19RABO03992844" (authorized\/beneficiary) "Carl"
Carl is authorized for or stands to benefit from account NL19RABO03992844.
"RS746620" (authorized\/beneficiary) "Ann"
Ann is authorized for or stands to benefit from account RS746620.
"DE9382991" (authorized\/beneficiary) "Bob"
Bob is authorized for or stands to benefit from account DE9382991.
authorized/\beneficiary = {("RS746620", "Ann")}
authorized\/beneficiary =
{ ("NL19RABO03992844", "Carl")
, ("RS746620", "Ann")
, ("DE9382991", "Bob") }
Fact
Fact
"Peter" traveler~ "LBD-199"
"LBD-199" traveler "Peter"
"Peter" traveler~ "TSS-730"
"TSS-730" traveler "Peter"
"TSS-730" dest "Rome"
"Rome" dest~ "TSS-730"
"TSS-730" dest "Paris"
"Paris" dest~ "TSS-730"
"QRA-492" dest "Paris"
"Paris" dest~ "QRA-492"
Formal statement
Elaborate natural language
Ordinary natural language
"Peter" (traveler~/dest~) "Rome"
For each trip, if it has destination Rome, then it has been made by Peter.
Every trip to Rome has been made by Peter.
statements
"NL19RABO03992844" beneficiary "Carl"
"DE9382991" authorized "Bob"
"RS746620" authorized "Ann"
"RS746620" beneficiary "Ann"
A concept statement defines a concept in natural language. A concept is a name for similar things. For example: Peter
, John
, and Barack
are things you might want to call Person
, whereas 45-NP-88
and KD-686-D
could be instances of the concept LicensePlate
.
This statement may occur anywhere within a context, either inside or outside a pattern.
This statement means that there exists a concept called <Uppercase identifier>
in the current context.
<Uppercase identifier>
specifies the name of the concept.
String
contains a definition of the concept. This definition is used by the documentation generator, which expects it to be a grammatically correct and complete sentence.
String?
is an (optional) reference to the source of the definition. It is meant for traceability.
The name of a concept starts with an uppercase.
A concept should be used for immutable concepts. E.g. use a concept Person
to express that a person will always be a person and will not change in, let us say, a table. However, don't use Employee
, because termination of an employee's contract causes a person to be an employee no longer. So employees are not immutable. To be an employee is a dynamic property, so model it as a relation.
The description will be printed in the functional specification, so please check that your definition is a complete sentence.
Concepts need not be defined. If you use a concept without a definition, Ampersand defines it for you (regardless of whether you defined it or not).
PURPOSE
-statements abundantlyThe purpose of a PURPOSE
-statement is to explain why something exists. Readers of your script will understand the script ten times better if they know why things exist. A good practice is not to cut corners and specify your purposes carefully and for as many things as you can.
Managing requirements can be difficult when different stakeholders have different concerns, individual users have strong opinions of their own, requirements are changing over time, or users are unable to articulate requirements when you most need it.
Now, let us assume that hurdle is taken and you have an approved list of requirements. How does this differ from specifications? In Ampersand, you state things as they will be built. A statement in Ampersand specifies your information system precisely. If you generate a system directly from your Ampersand script, the resulting system will help users to keep all rules satisfied. If software engineers build the system to specification, they must guarantee nothing less.
Requirements tell what users say they want. Specifications define unambiguously what to build. So write prose in your requirements and write Ampersand as your specification.
The pitfall is to mistakenly use the list of requirements as specifications. This is not uncommon, so be warned. Well-known consequences are confusion among software engineers, scope creep, and project overruns. Failure to make this distinction is an early warning for project failure.
A working Ampersand prototype and an understandable specification document are compelling evidence of the completeness of your specification. Especially if both have been generated from the same Ampersand script. Fortunately, requirements articulation takes a while so in many cases you can have your specification done by the time the list of requirements is approved. This means you can make the specifications as requirements are developing, not causing any delays other than needed for the requirements.
Making a good Ampersand script is difficult. Working in pairs increases your speed for a number of reasons:
you learn from each other. Switch pairs every now and then, if possible.
Developing thoughts works better and faster if you work together. Develop thoughts by discussing, challenging, and trying things in practice.
The ontology serves to codify language. Don't hesitate to change it as insights develop. Especially database builders who are careful to change their data models might have to acquire new reflexes, as for them the ontology may feel like a data model.
Start with the tiniest thing that works. As you expand it, see to it that it stays working. Taking tiny steps, compile and run eaoch one before taking the next step. Keep an eye on your data model as your model grows. And run a diagnosis every now and then.
Work towards things that work in every situation without exception. Leave out frills and fringe, because they will hinder future reuse. Do not hesitate to start over (refactor), because your design usually gets better.
Consider each choice and every decision you make carefully from a maintainer's perspective. Will your code be adaptable in five years' time, when you, your team, and all other stakeholders have been replaced by others?
More general solutions are more widely applicable, but also more abstract. More specific solutions are easier to understand because they are more concrete. Choose the right abstraction level, keeping your audience in mind.
Doing things right the first time pays off.
Names should remind the reader of the intended meaning. For that to happen, describe meanings concisely and precisely and choose short names that will remind readers of that meaning.
Rules without purpose may block transactions without proper cause, frustrating your user. Or they may produce avoidable work for them. With rules, less is better. Focus on rules that are strictly necessary.
A relation statement says that there exists a relation in a context. It introduces (defines, declares) the relation in the context. Each relation is a set that contains pairs of atoms. Over time, pairs can be inserted into or deleted from a relation.
A relation statement can have one of the following forms:
The second and third ways will become obsolete in future versions of Ampersand.
All three ways define a relation by its name, its source concept and its target concept. The name of a relation is a single word that starts with a lower case letter. The source and target concepts start with an upper case letter.
A relation statement may occur anywhere inside a context, both inside and outside of a pattern.
The optional <properties>
, <pragma>
, and <meaning>
-parts are discussed in the sequel.
A relation statement means that there exists a relation in the current context with the specified name, source concept and target concept.
The name, source concept and target concept together identify a relation uniquely within its context. As a consequence, the name of a relation does not have to be unique. E.g. name[Book*Name]
can be specified in the same context as name[Person*Name]
. Because they have different source concepts, these are different relations.
The <properties>
-part is meant for writing multiplicity constraints in a comma separated list between square brackets '[' and ']'. E.g. [UNI,TOT]
. The following multiplicity constraints are available:
UNI
(univalent)
INJ
(injective)
SUR
(surjective)
TOT
(total)
SYM
(symmetric)
ASY
(antisymmetric)
TRN
(transitive)
RFX
(reflexive)
IRF
(irreflexive)
PROP
(property)
Let's assume that we want to express that any person can live in one city only. So under this constraint "Joe Smith lives in New York" and "Joe Smith lives in Denver" cannot both be true at the same time.
In relation algebra, we say that the relation is univalent, which means that every atom in the source concept can only be paired with a single atom in the target concept. This is modeled as
A pragma is optional and is characterized by the reserved word PRAGMA
. The PRAGMA
is followed by two or three strings. It is used to construct sentences in natural language, using pairs from the actual population of a relation. A pragma specifies how we speak (in natural language) about any pair in the relation. Ampersand also uses pragmas to generate examples in the functional specification. Example of a pragma with three strings:
To use this pragma on the pair (John,Amsterdam)
results in the sentence "Student John flies the flag of Amsterdam in top."
. The two atoms are fitted in between the three strings. A pragma with two strings is identical to a pragma in which the third string is empty.
(The PRAGMA
keyword will become obsolete in a future version of Ampersand. It will be replaced by the VIEW
-statement which offers more flexibility in composing sentences.)
Example:
The PRAGMA
tells us that it makes sense to utter the phrase "Provider Mario's Pizza's has accepted order 12345."
A meaning is optional and is characterized by the reserved word MEANING
. It specifies the meaning of a relation in natural language. It is is meant to say in natural language what it means for a pair to be in the relation. The meaning is used to generate documentation with and is printed in the functional specification. A <meaning>
has the following form:
The <text>
-part is where the the meaning is written down. We support both:
a string, enclosed by double quotes,
e.g.
any text, starting with {+
and ending with +}
e.g.
The optional <language>
is specified as
IN ENGLISH
or
IN DUTCH
.
Example :
This is a way to override the default language.
Sometimes you need formatting in the meaning, such as dotted lists, italics, or mathematical symbols. For this purpose you have a choice in which syntax you specify the meaning. The optional <markup>
is one of :
REST
(Restructured text)
HTML
LATEX
MARKDOWN
Example :
Ampersand uses Pandoc to offer a choice for your markup. See pandoc.org for details.
Contexts exist in Ampersand for the purpose of dealing with truth. Within one context there may be no contradictions.
Any statement can be true in context only. Within one context, there are no contradictions. As facts are true statements, we say that facts must exist inside a context.
Examples of contexts:
a single lawsuit in which all case data is contained;
the financial administration of a repair shop;
the life insurance department of a bank.
The world is full of contradictions. Examples:
Bob's personal income over March 2013 according to Bob's employer differs from Bob's personal income over March 2013 according to the National Tax Authority. (To resolve this, we must distinguish between the context of Bob's employer and the context of the National Tax Authority.)
The police can be convinced that Peter X commited the crime, yet his attorney is convinced he is innocent. (To make sense of the situation, a judge distinguishes the reasoning of the defense from the reasoning of the prosecution as different contexts. In fact, the judge will construct her own context to create the verdict )
A computer system can tell that the person with social security number 721-07-4426 was born on April 27th, 1943, while the same computer system tells in another screen that this person was born on May 3rd, 1952. This is inconsistent, because every person has only one birth date. (This situation should be reported as a software mistake.)
A context is specified by the context elements between the keywords CONTEXT
and ENDCONTEXT
. A context has a name. You can optionally specify the language and markup (see below).
A context represents a set of true statements in a given language, which is the meaning of that context.
To tell Ampersand what language your context is in, you can append a language directive to your context. Currently English and Dutch are supported. To do so, directly following the name of your context, you can specify
Where <language>
can be ENGLISH
or DUTCH
.
Directly following the optional language definition, you can optionally specify the format of texts in your PURPOSE
statements and MEANING
blocks. This allows you to use your favourite markup language within Ampersand, as long as it is one of REST
(Restructured Text), HTML
, LATEX
or MARKDOWN
. If you specify one of these words in your CONTEXT
definition, that value will be the default markup of all purposes and meanings in your context.
(For details on these formats, see pandoc.org).
A context may contain different types of statements. The order of statements in a context is irrelevant for the software that Ampersand generates. However, the order is maintained when documentation is generated.
The module system in Ampersand is currently being developed. It isn't yet in the main branch. This page is being created as work progresses.
See https://github.com/AmpersandTarski/Ampersand/issues/1307 for details.
An Ampersand specification consists of a CONTEXT file and a set of Modules. A module in Ampersand serves the dual purpose of controlling name-spaces and ordering definitions in multiple files.
We have been inspired by the module system of Haskell.
To tell Ampersand what language your module is in, you can append a language directive to your module. Currently English and Dutch are supported. To do so, directly following the name of your context, you can specify
Where <language>
can be ENGLISH
or DUTCH
.
Directly following the optional language definition, you can optionally specify the format of texts in your PURPOSE
statements and MEANING
blocks. This allows you to use your favourite markup language within Ampersand, as long as it is one of REST
(Restructured Text), HTML
, LATEX
or MARKDOWN
. If you specify one of these words in your CONTEXT
definition, that value will be the default markup of all purposes and meanings in your context.
(For details on these formats, see pandoc.org).
An IMPORT statement contains a reference to another module and contains information of what definitions are imported from that module together with information about possible name-changes.
A module may contain different types of statements. The order of statements in a context is irrelevant for the software that Ampersand generates. However, the order is maintained when documentation is generated.
To keep this chapter as readable as possible, we have chosen to omit some details that are irrelevant for practically all &-modelers. In the very rare case that these technicalities are of interest, the reader could have a look in , where all EBNF statements are in comments.
This page is meant as a reference for syntactical details and conventions, reserved words, etc.
The most effective way to learn Ampersand's syntax is to copy from existing scripts. This is learning by examples. This reference chapter is suitable to check things, and less suitable for learning.
Ampersand has reserved words, such as RELATION
, CONTEXT
, CONTAINS
. All reserved words are written in capital letters. They are introduced on the fly. You will find an exhaustive list of reserved words at the end of this page.
Untyped atoms are written between double quotes, e.g. "Peter"
or "KD-686-D"
. If you want to introduce a double quote inside an atom, escape it with a backslash, e.g. "the symbol \" is called double quote"
.
Numeric atoms always start with a digit, e.g. 4711
or 75.88E3
. The boolean atoms are TRUE
and FALSE
. Dates and timestamps follow the Excel-syntax, e.g. ??? The atom _SESSION
indicates the current user session, and is an instance of concept SESSION
. It is used in services.
Brackets must always match. For terms, we use round brackets (
and )
. For populations and services we use square brackets [
and ]
.
Constructs that contain ampersand statements are contexts and patterns. They always come in pairs: PATTERN
and ENDPATTERN
, and CONTEXT
and ENDCONTEXT
.
White space characters (spaces, tabs, CRLF) are meaningless. You can use them freely to layout your script in a way that helps you to recognize its structure.
A comment on a single line starts with --
. Everything after a --
symbol is ignored until the line ends. Multiline comments are wrapped between comment brackets {-
and -}
. Multiline comments may be nested.
Identifiers always start with a letter. Concepts start with a capital letter, as in Person
, Case
, A
, and Order
. Relation names start with a lower case letter, as in contains
, attr
, sessionLogin
, or r
.
terms are combined with operators. Binary operators may require brackets to avoid ambiguity. To save writing unneccessary brackets, some precedence rules are in place.
Within an operator category, you must place brackets to disambiguate. E.g. r/\s\/t
is not allowed. You have to write either (r/\s)\/t
or r/\(s\/t)
. Across categories, you may omit brackets because a higher precedence binds stronger. So r;s\/t
means (r;s)\/t
. (Note that (r;s)\/t
and r;(s\/t)
have different meanings). Associative operators (\/
, /\
, ;
) need not be disambiguated with brackets. So r\/s\/t
and (r\/s)\/t
and r\/(s\/t)
all mean exactly the same.
Keywords in Ampersand are always written in CAPITALS.
Keywords for the main structure of the code
ENDCONTEXT
ENGLISH
DUTCH
META
THEMES
ENDPATTERN
PRAGMA
UNI
INJ
SUR
TOT
SYM
ASY
TRN
RFX
IRF
PROP
CONTAINS
RULE
MESSAGE
VIOLATION
TXT
SRC
TGT
I
V
ONE
ROLE
MAINTAINS
Keywords for documentation
REF
REST
HTML
LATEX
MARKDOWN
INTERFACE
FOR
LINKTO
BOX
Keywords for identities
Keywords for views
VIEW
ENDVIEW
DEFAULT
TEMPLATE
HTML
Keywords for generalisations:
CLASSIFY
ISA
IS
Keywords for TType:
REPRESENT
TYPE
ALPHANUMERIC
BIGALPHANUMERIC
HUGEALPHANUMERIC
PASSWORD
BINARY
BIGBINARY
HUGEBINARY
DATE
DATETIME
BOOLEAN
INTEGER
FLOAT
AUTOINCREMENT
Reserved words for values of atoms:
TRUE
FALSE
--for booleans
_SESSION
Reserved words for concepts
ONE
SESSION
Experimental keywords:
SERVICE
EDITS
Deprecated keywords:
SPEC
KEY
PROCESS
ENDPROCESS
Keywords for
Keywords for
Keywords for
<meta>
a statement to provide metadata to a script, such as author, company, etc.
<pattern>
a block of code that represents rules on a single and specific topic, at the user's discretion
<rule>
a statement that declares a rule
<gen>
a statement that specifies generalization/specialization of concepts
<relation>
a declaration of a relation, stating the existence of a relation within the context
<conceptDef>
a description of a concept, to document its meaning
<representation>
a statement that defines the atomic type of a concept
<roleRule>
a statement that makes a role responsible for satisfying a rule
<viewDef>
a statement for presenting facts in a readable sentence
<service>
a unit of code that can be run independently and specifies interaction with a user or a computer
<purpose>
a statement to describe the purpose of a context or a context element
<population>
a statement that sums up the initial population of a relation
<include>
a statement to include another file in the context
<pattern>
a block of code that represents rules on a single and specific topic, at the user's discretion
<rule>
a statement that declares a rule
<gen>
a statement that specifies generalization/specialization of concepts
<relation>
a declaration of a relation, stating the existence of a relation within the context
<conceptDef>
a description of a concept, to document its meaning
<representation>
a statement that defines the atomic type of a concept
<roleRule>
a statement that makes a role responsible for satisfying a rule
<viewDef>
a statement for presenting facts in a readable sentence
<service>
a unit of code that can be run independently and specifies interaction with a user or a computer
<purpose>
a statement to describe the purpose of a context or a context element
<population>
a statement that sums up the initial population of a relation
operator category | precedence | operators |
logic | 1 (weakest) | |- (subset), |
binary boolean | 2 |
|
binary relational | 3 |
|
unary prefix, unary postfix | 4 (strongest) |
|
RJ/20161220: the CONTEXT statement is currently being revised. It is expected that this will lead to changes in syntax in the course of 2017.
The data contained in a business system represents a view of (a very small part of) the real world. Ideally, this view must be consistent, meaning that there may be no contradictions within that view. Since different business systems have different ways of viewing the real world, and/or look at different parts of the real world, we need to be able to distinguish between such views. We use the term 'Context' to refer to an individual view. Thus, a Context is defined in terms of concepts, relations and rules, and it consists of atoms and links to populate them.
Any Ampersand model has one context. The model is true within its context and there is no knowledge in a model about other contexts.
The model is specified between the keywords CONTEXT and ENDCONTEXT. A context has a name.
Other models included with the INCLUDE statement become part of the context they are included in.
To tell Ampersand what language your context is in, you can append a language directive to your context. Currently English and Dutch are supported. To do so, directly following the name of your context, you can specify
Where can be ENGLISH
or DUTCH
.
Directly following the optional language definition, you can optionally specify the format of your texts (see PURPOSE statement). Ampersand understands some different markup styles. The default style is REST (Restructured Text)
where can be one of
REST
,
HTML
,
LATEX
,
MARKDOWN
.
(For details on these formats, see pandoc.org).
To facilitate reusing code, Ampersand allows its user to divide code over different files.
The INCLUDE
-statement includes the code of another Ampersand-script or the data of a .xlsx-file into the context.
This statement specifies files that need to be included before compiling. The filename is given in double quotes, including a path that is relative to the position of the main adl-file. The main adl-file is the file that is called with the command Ampersand.
Possible files to include are:
other adl-files
xlsx-files to include population
All code in the included adl-files will become part of the context of the main adl-file.
Make sure to include the adl-files before including xlsx-files.
Included files may contain INCLUDE
statements themselves. The files mentioned there are treated as though they were included in the main file. So their code is also part of the same context. Nested adl-files can have their own xlsx-files included.
For formatting your excel-file see the text on the Excel Importer.
To store data in a database corresponds to populating the relations in a context. Atoms are the data and pairs of atoms are inserted and deleted during the lifetime of a relation.
All pairs in a relation are called the population of that relation. All atoms in a concept constitute the population of that concept. The population of all relations and concepts in a context make the population of that context.
There are two ways to populate a concept with atoms:
A POPULATION
statement defines the initial population of a concept or a relation.
An INCLUDE
statement defines the initial population from an xlsx-file (i.e. an Excel speadsheet)
Using spreadsheets to define an initial population allows you to work with larger populations. Often you can use an existing spreadsheet and adapt it to become acceptable as Ampersand input.
You can define atoms separately and you can define the pairs in a relation. Both methods result in added population for each concept.
The list of pairs is a comma-separated list between square brackets. Pairs are comma-separated pairs between round brackets. Each atom is enclosed in double quotes.
The purpose of a rule is to constrain data. Refer to the chapter about rules in the tutorial for examples and a practice oriented explanation.
A rule statement defines something that should be true. It does not define the enforcement.
A <rule>
has the following syntax:
A <label>
is optional. It can be a single word or a string (enclosed by double brackets) followed by a colon (:
).
An expression can be any of:
Expression BinaryOperator Expression
UnaryOpPre Expression
Expression UnaryOpPost
a (reference to a) relation (including an optional signature, when required to disambiguate):
A relation by name
I
(the Identity relation)
V
(carthesian product) Note that this can also be used to denote the empty relation, by using the unary negation operator: '-v'
A singleton expression (the value of an atom)
an expression enclosed in brackets.
The following operators are available to build expressions:
Binary operators
equivalence: =
composition: ;
inclusion: |-
intersection: /\
union: \/
difference: -
left residual: /
right residual: \
diamond: <>
relative addition: !
cartesian product: #
Unary operator (pre-operator)
complement: -
Unary operators (post-operator)
conversion (flip): ~
Reflexive, transitive closure: *
(Kleene star) --currently not implemented
transitive closure: +
(Kleene plus) --currently not implemented
The meaning of a rule can be written in natural language in the Meaning part of the RULE statement. It is a good habit to specify the meaning! The meaning will be printed in the functional specification. The meaning is optional.
The <text>
part is where the the meaning is written down. We support both:
a simple string, enclosed by double quotes
any text, starting with {+
and ending with -}
The optional language is specified as
IN ENGLISH
or
IN DUTCH
.
The optional Markup is one of :
REST
(Restructured text)
HTML
LATEX
MARKDOWN
If you need specific markup, there are several options to do so. The default markup is used, but you can override that here. We rely on Pandoc to read the markup.
Messages may be defined to give feedback whenever the rule is violated. The message is a predefined string. Every message for a rule should be for another Language.
A violation message can be constructed so that it gives specific information about the violating atoms:
Every segment must be of one of the following forms:
TXT
String
SRC
Expression
TGT
Expression
A rule is violated by a pair of atoms (source, target). The source atom is the root of the violation message. In the message the target atoms are printed. With the Identity relation the root atom itself can be printed. You can use an expression to print other atoms. Below two examples reporting a violation of the rule that each project must have a project leader. The first prints the project's ID, the second the project's name using the relation projectName:
VIOLATION ( TXT "Project ", SRC I, TXT " does not have a projectleader")
VIOLATION ( TXT "Project ", SRC projectName, TXT " does not have a projectleader")
By default rules are invariant rules. By preceding the rule statement with a role specification for this rule, the rule becomes a process rule.
tbd
Most things in your model are in it for a reason. To document these, you should use the PURPOSE statement.
PURPOSE
<type of thing>
<name>
<language>?
<markup>?
<list of references>?
{+
<anything>
+}
Where <type of thing>
and <name>
are the type and name of the thing that is refered to. This could be one of: CONCEPT
, RELATION
, RULE
, IDENT
, VIEW
, PATTERN
, INTERFACE
, CONTEXT
The optional and can be used to override the settings for language and markup. If omitted, these are inherited from the pattern of context where the PURPOSE statement is specified in.
Patterns are meant to isolate discussions and make solutions reusable, as known from design patterns.
A pattern is a set of rules that describes a theme or a general reusable solution to a commonly occurring problem.
For instance, if specific concerns about security arise, you might want to discuss this with stakeholders in security. With them you can discuss which rules in particular constitute your solution. Divide your problem in smaller pieces and discuss each piece with just the right stakeholders. This allows you to go deeper by talking to the right people. It saves time as well by freeing others from having to participate. An even larger benefit arises if you reuse patterns that have been discussed and scrutinized before. The best thing comes once your stakeholders agree. By that time, your pattern represents their agreement formally in Ampersand, so you can use it in the larger context of the information system.
Every pattern has the following form:
A pattern consists of any number of pattern elements in an arbitrary order. The following pattern elements are allowed:
A model can have as many patterns as you want. It has no effect on how the code is processed.
The service definition must be outside a pattern
A pattern contains rules in an arbitrary order. The context in which these rules are valid must contain the definition for each of the relations that are used in those rules. It is good practice to declare all relations in the pattern itself. That practice makes the pattern self-contained and therefore more suitable for reuse.
Ampersand advocates one theme in one pattern. Stakeholders confine their discussion to one theme, and deliver the result in one pattern.
In the current implementation of Ampersand, patterns are defined within a context. (This will change in a future version.) If you want to reuse patterns, you have to cut-and-paste them from one context to another. In the future, there will be a better mechanism for reusing patterns in different contexts.
Services are meant to expose functionality and data from a context, to let users or information systems interact with the system by creating, reading, updating, and deleting data.
Note: The service definition must be outside a pattern
The following figure is an example of a user interface, which shows the name, status, e-mail and co-workers of a person called "J. Lovell".
The specification of this user interface is given in the following service definition:
To understand this fragment, take notice of:
The name of this service is Person
. This name immediately follows the keyword INTERFACE
.
The term following the colon, I[Person]
, is the interface term of this service.
The service can be applied to any atom from the domain of the interface term. So this particular service is applicable to any atom of type Person
. In the screenshot, it applies to "J. Lovell"
.
The labels "Name", "Status", "Email", and "Works with" correspond to field names in the user interface.
Each term at the right of a field name specifies which data is presented in the field. For this reason it is called the field term for that field. Field name and field term are separated by a colon.
Of all pairs <"J. Lovell", x>
from the field term, the field displays the right atom x
. A field term always works on one specific atom on the left, which is "J. Lovell"
in this example.
Field terms are subject to type checking. The following relations provide an example for getting a type-correct service:
The source concepts of a field term must match the target concept of the interface term.
Looking at the screenshot, we can tell that "J. Lovell"
has one personName (which is "J. Lovell"
), it has no personStatus, one personEmail and three persons to work with in RELATION workswith
.
You can create structure in a service by nesting. Here is an example:
The specification of this service is given in the following code fragment.
Notice the following features:
1. The structure of a service is hierarchical. It consists of boxes within a box. This is because a field term may be followed by a BOX
with a list of subservices. Without it, it is just a field term. 2. When a field term is followed by a BOX
, every atom in the codomain of the field term is displayed in a box of its own on the screen. That box behaves like a service with the field term serving as interface term of that subservice. 3. By this mechanism, the hierarchical structure of the entire service translates directly to the hierarchical structure of the web-page in which it is displayed. 4. The source concept of a field term must match with the target concept of the field term outside the box.
5. The target concept of a field term that has a box, must match with the source concepts of each field inside that box.
Especially in more complicated services, you will find it nice to adapt the layout of the fields in the user interface. For this purpose, you can substitute the word BOX
by COLS
, ROWS
, or TABS
, as in the following code fragment. Note that these annotation have no meaning other than to change what the user interface looks like.
Notice the effect that these changes have on the user interface.
Notice the following features:
1. The keyword TABS
turns the box into a tabulated layout.
2. The keyword COLS
turns the layout 90 degrees into columns.
3. The keyword ROWS
is default for any box. It does not change the effect of BOX
.
Compile and run the script Project Administration Example. Start by reproducing everything that is shown above. It is quite likely that you will be trying out your own ideas before you get to the end... Have fun!
After finishing your assignment, you have learned:
to explain how a service definition is displayed on the screen of a user.
to predict which data items a service applies to, if you know which pairs are in an interface term.
to predict which data items are displayed, if you know which pairs are in a field term.
to explain which atoms are used in a sub-interface.
to understand what the keywords TABS
, COLS
, and ROWS
do to your display.
More than one service may apply to the same atom. That gives you a choice on runtime to which service you want to navigate. If no service applies, that atom is not navigable.
To generate documentation, Ampersand is language aware.
Ampersand assigns a language to every text written as documentation, whether it is a MEANING
, PURPOSE
or other text except comment.
Ampersand does not recognize any language, so you must tell which language is meant. To tell Ampersand what language you use, you can append a language directive to a context, a meaning, and to a purpose statement. Currently English and Dutch are supported.
A language directive has the following syntax
Where <language>
can be ENGLISH
or DUTCH
.
The first example is a context declaration in which the language ENGLISH
is specified.
This means that all natural language elements within this context are written in ENGLISH
, unless specified otherwise.
The second example is a MEANING
, which can be used in a RULE
statement and in a RELATION
statement. This example uses a MEANING
in ENGLISH
:
The language directive IN ENGLISH
means that the meaning of the relation ptpic[Pattern*Image]
is written in ENGLISH
.
The third example is a PURPOSE
statement in which the language DUTCH
is specified.
This means that the contents of this purpose statement is written in DUTCH
.
Ampersand assumes that whatever is written is written in the language denoted in the language directive. It doesn't check whether that language is actually used, because it cannot recognize languages.
If a CONTEXT
has no language directive, IN ENGLISH
is used by default. If a CONTEXT
has a language directive, that language will be the default language of all natural language items within that context.
If a PURPOSE
statement or a MEANING
has no language directive, Ampersand assumes this to be the language of its context. So, the user needs to specify a language only if it is an exception to the default.
Documentation generated by the Ampersand-compiler is written in a single language, which is specified when the compiler is called.
This statement is a rule, which defines an identity on a concept. It is syntactic sugar for specifying a set of relations that identify atoms in a specific concept. For example, if relations pi
and rho
determine an atom of concept T
uniquely, you can write:
As the IDENT statement defines a rule, it can be in the same places as any other RULE.
where:
<label>
is the name of the rule. It can be a single word or a string (enclosed by double brackets). It is followed by a colon (:
) to distinguish the label from the concept that follows.
<Concept>
is the name of the Concept for atoms of which the rule specifies an identity
Between brackets are terms whose source concept must be <Concept>
. This is enforced by the type system.
translates into the following rule:
Note that
in case everye
is both univalent and total, e<>e~
equals e;e~
, and the rule is equivalent to:
in case every e
is univalent but not total, you should use the IDENT
statement (or the rule that it implements), because that also works when an e
is not populated.
This statement will be available in Ampersand vs. 4.
This statement provides syntactic sugar for defining tabular information conveniently. It introduces a number of relations and rules in a single statement, to simplify a script.
where:
<label>
is the name of the rule. It can be a single word or a string (enclosed by double brackets). It is followed by a colon (:
) to distinguish the label from the concept that follows.
<Concept>
is the name of the Concept for atoms of which the rule specifies an identity
Between brackets are terms whose source concept must be <Concept>
. This is enforced by the type system.
translates into the following declarations:
Multiplicity annotations are allowed. For example:
translates into the following declarations:
This statement makes nice combinations with the IDENT statement. For example to define two identities for persons:
This states that a person is uniquely defined by ssn
, but also by the combination of name
, birthplace
, and birthdate
. This statement can also be used to objectify (reify) an term e
If a user is tempted to replace the Create/Delete pair with a single equivalence, this becomes:
To facilitate reusing code, Ampersand allows its user to divide code over different files.
The INCLUDE
-statement includes the code of another Ampersand-script or the data of a .xlsx-file into the context.
This statement specifies files that need to be included before compiling. The filename is given in double quotes, including a path that is relative to the position of the main adl-file. The main adl-file is the file that is called with the command Ampersand.
Possible files to include are:
other adl-files
xlsx-files to include population
All code in the included adl-files will become part of the context of the main adl-file.
Make sure to include the adl-files before including xlsx-files.
Included files may contain INCLUDE
statements themselves. The files mentioned there are treated as though they were included in the main file. So their code is also part of the same context. Nested adl-files can have their own xlsx-files included.
In this section we will make an Ampersand script that is based on an existing spreadsheet. This technique is useful for quickly adding population to an information system. Ampersand has a facility that allows you to import existing .xlsx files with minimal changes.
We can consider Ampersand as a finite system of relations. Every relation is a set of (ordered) pairs and each pair contains two atoms. However, in the real world we also store information in wider tables, as we do in spreadsheets and relational databases. Here is the trick. If we have two pairs that share the same left atom, e.g. (1, Abraham) and (1, Lincoln), we can put them in the same row. Using the same trick, we can interpret a row in a spreadsheet as a number of pairs.
Let us look at an example:
Since Ampersand works with relations, it must represent this table as relations. Three relations can do the job in the following manner:
Notice that the column names in the table correspond with the relation names in Ampersand. In the table we call them "attributes". So it makes sense to say that a relation in Ampersand can correspond with an attribute in a table.
In theory, the population of the Hawaii-script might just as well be given in a spreadsheet. This works in practice too. It looks like this:
Please copy this in a spreadsheet of your own. The element in the first column with square brackets tells Ampersand that a new table starts. The first row contains relation names. The second row contains concept names. The rows that follow contain pairs. Ampersand reconstructs those pairs as in the example above.
In practical applications, you might want to reuse data from existing spreadsheets. People tend to have lots of "informal administration" in spreadsheets, which gives you access to authentic population. Surely you need that data organized in rows, but fortunately that is reasonably common. In such cases, you just add two lines above each table to inform Ampersand about the relations that are populated. In other cases, you have some work organizing the spreadsheet for importing it.
You will find the Excel import function in the menu bar on the top right of your screen
.
This is what your upload screen looks like:
You can upload one or more .xlsx-files by dropping them in the drop zone or by selecting them. You have to upload the population with the green
Upload
button. At that time, all population from the .xlsx-file is added to the context and checked for inconsistencies. As a result, you may get errors when uploading. Only error-free spreadsheets will be uploaded successfully. As long as an error remains, the population in your context will not change.
Make a population of your own for the Hawaii-script and put it in a .xlsx spreadsheet. As described above. Make sure to delete the population statements from your Hawaii source code, to make sure that you get to see the population from your .xlsx-file. Generate a prototype from your Hawaii-application, upload your population in Excel and play around with the results.
After finishing your assignment, you have learned:
to upload population to your Ampersand application in the form of a spreadsheet in .xlsx-format;
to understand how a POPULATION
-statement relates to the contents of a spreadsheet;
that the contents of the spreadsheet is added to the population of your context, provided this does not lead to any conflict.
Documentation generated by RAP3 is written in DUTCH
. Natural language items written in any other language are ignored. This is , but a feature. RAP3 only "speaks Dutch" and ignores anything else.
For formatting your excel-file see the text on .
<rule>
a statement that declares a rule
<classify>
a statement that specifies generalization/specialization of concepts
<relation>
a declaration of a relation, stating the existence of a relation within the context
<conceptDef>
a description of a concept, to document its meaning
<representation>
a statement that defines the atomic type of a concept
<roleRule>
a statement that makes a role responsible for satisfying a rule
<ident>
a rule that defines an identity on a concept
<viewDef>
a statement for presenting facts in a readable sentence
<purpose>
a statement to describe the purpose of a pattern or a pattern element
<population>
a statement that sums up the initial population of a relation
[Subject] | pass | required |
Subject | Student | Destination |
Surfing | Brown | Hawaii |
Surfing | Conway |
Latin | Brown | Rome |
World Religions | Applegate |
World Religions | Brown | Rome |
firstname | lastname | birth |
1 | Abraham | Lincoln | February 12, 1809 |
2 | Barack | Obama | August 4, 1961 |
3 | Calvin | Coolidge | July 4, 1872 |
4 | Dwight | Eisenhower | October 14, 1890 |
The syntax of a service is best explained by means of examples. However, if you want to understand the syntax in detail, this section is what you are looking for.
A service specification has the following structure. It is identical for user interfaces (INTERFACE
) and application programming interfaces (API
).
The name of a service must be unique within the context. The term defines the atoms to which the interface can be applied. The (optional) crud annotation constrains the possible interactions a user can do. The (optional) views determine what the service will look like. If no view is specified, the service will look like the screenshot above. Finally the sub-interface contains all the contents, i.e. the fields, field names and the constraints on them.
The hierarchy of boxes in a service comes from the following (recursive) syntax of <subinterface>
.
A sub-interface may be defined on the spot (by <boxKey> <box>
) or it may link to another service to reuse its structure:
The boxKey is meant to tell the front-end application what the service looks like. The compiler uses templates to adapt an interface to specific needs regarding its HTML structure. Please read the documentation of templates for details.
If no htmlname is specified, Ampersand uses BOX <FORM>
by default.
A box is simply a list of service items (ifcItem
) separated by commas. Each service item specifies a field in the service or a sub-interface.
Each service item has a label that must be unique within the box. After the colon there is either a term or a text. The term specifies which data is related to the field it specifies if it has no sub-interface. If it does, it specifies the atoms on which the box is applied.
Suppose we have a delivery-hub that distributes orders over vendors and registers the subsequent deliveries. Let us define a service for clients, to allow clients to change their name and address and display their orders.
The service has a header, which is the first line in this example:
The word ClientInfo
is the name of this service. This name identifies the service , so it must be unique throughout the entire context.
This service is shown only to users with roles Client
or Vendor
. That is indicated by the restriction FOR Client,Vendor
. Without that restriction, the service is available for every user in any role.
The term to the right of the colon (:
) symbol is called the interface term. A service is called from an atom which must be in the domain of this term. Let, for example, Peter
be a Client
. As Peter
is an element of the domain of I[Client]
, the service can be called from that atom.
The same term, I[Client]
, is also used as box term for the box that follows the header. For every element in the codomain of the box term, a container (in HTML: <div>
) will be drawn on the user screen. That box serves as a subinterface, which is called with precisely one atom. With I[Client]
as box term, the codomain will contain just one atom, which is precisely the atom from which the service was called.
In this example, the outermost box contains seven box items and the innermost box two. Each box item has a label and an term. For example the box item "Name" : clientName
has "Name"
as its label and clientName
as term. The atom a
from which the box was called is used to select the pairs (a
,x
) from the term. All x
-es for which (a
,x
) is in clientName
will be displayed. Supposing that the relation clientName
associates only one name to a client, this specific box item displays just one name. However, in the fifth box item, the term orderedBy~ - V; orderAccepted~
may contain an arbitrary number of orders to be accepted by provider, all of which are shown.
A service is a component of an information system that exposes functionality and data from a context, to let users or information systems interact by creating, reading, updating, and deleting data.
A service is a component of an information system. Its life starts when it is deployed and ends when it is pulled back. A typical instance is a user interface based on HTML-CSS that runs in a browser. But an application program interface (API) that serves other computers with web services is a perfectly valid instance as well.
The definition of a service specifies which data is presented to which users. For every different use of the system a different service can be defined. This may lead to a substantial amount of services for large and complex systems. However, one device will show one service only at any given moment in time.
This page gives syntactic details of services. Some more explanations are found here.
Please note that the keyword INTERFACE
is still used. That may be confusing. In a future release of Ampersand the keyword INTERFACE
will become obsolete and the word SERVICE
will be used.
This example specifies three tabs. One shows students, one shows courses and one shows modules. This is what it looks like when run in a browser:
Due to the complexity of services, its syntax and meaning are discussed in a separate section.
On the user screen each atom is displayed in some form as data. If a service exists for that atom, that is shown to the user as a hyperlink to which you can navigate.
When running an application in your browser, you are watching one user interface at any given moment in time. Each hyperlink on your screen represents an atom to which some service applies. To navigate to that user interface, you click on the hyperlink. You will see the service being applied solely to the atom you just clicked. To determine the atom(s) to which a service applies, each service has an interface term.
The next sections contain two examples:
a client service to allow clients of a web shop to change their name and address and show them status information of their orders;
a login service to demonstrate how to get different interface structures under varying conditions.
Patterns are meant to isolate discussions and make solutions reusable, as known from design patterns.
A pattern is a set of rules that describes a theme or a general reusable solution to a commonly occurring problem.
For instance, if specific concerns about security arise, you might want to discuss this with stakeholders in security. With them you can discuss which rules in particular constitute your solution. Divide your problem in smaller pieces and discuss each piece with just the right stakeholders. This allows you to go deeper by talking to the right people. It saves time as well by freeing others from having to participate. An even larger benefit arises if you reuse patterns that have been discussed and scrutinized before. The best thing comes once your stakeholders agree. By that time, your pattern represents their agreement formally in Ampersand, so you can use it in the larger context of the information system.
Every pattern has the following form:
A pattern consists of any number of pattern elements in an arbitrary order. The following pattern elements are allowed:
A model can have as many patterns as you want. It has no effect on how the code is processed.
The service definition must be outside a pattern
A pattern contains rules in an arbitrary order. The context in which these rules are valid must contain the definition for each of the relations that are used in those rules. It is good practice to declare all relations in the pattern itself. That practice makes the pattern self-contained and therefore more suitable for reuse.
Ampersand advocates one theme in one pattern. Stakeholders confine their discussion to one theme, and deliver the result in one pattern.
In the current implementation of Ampersand, patterns are defined within a context. (This will change in a future version.) If you want to reuse patterns, you have to cut-and-paste them from one context to another. In the future, there will be a better mechanism for reusing patterns in different contexts.
TODO: This example is subject to bitrot. It has to be redone.
This example defines a login/logout service, because it is familiar. We show this example to demonstrate how to get different interface structures under varying conditions.
The compiler uses templates to adapt an interface to specific needs regarding its HTML structure. Please read the documentation of templates first for details.
To link system activities to a person or organisation, we use the notion of Account
. To log in means to associate a session with the Account
of the user. This association is made in the relation sessionAccount
. To log out means to break that link, i.e. to remove the session/account pair from relation sessionAccount
. When logging in, it is customary that the user identifies herself. In this example we do this with a UserID
and Password
.
A UserId
is used to identify the user by a unique name. In this way, the (system generated) key of the user in the database is kept within the database.
To make it more difficult to use an other person's Account
, the system registers passwords. A Password
is a string of characters known to the user only. For this reason, the login service must not expose the password while the user is typing it.
To isolate a data space for one specific user, we use the notion of session. A SESSION
corresponds with the notion of session as used in browsers. Ampersand links the session called '_SESSION'
to the current browser session, which results in the behaviour one would expect of a browser session.
A login service allows a user to log in and log out of the system. Here is what it looks like in a browser:
Wonder what the 25al1rdkdfvmapkkqvuf5sroc5 means? Well this is the session number of the actual browser session. It is the value for which the atom
"_SESSION"
stands in your script.
When you type your name, it shows up in the field Userid, but when you type in the password it is obscured by dots
(
as we would expect
)
:
When we then type
<enter>
, the login functionality disappears and the logout functionality appears:
When you click the checkbox, you have logged out and will return to the first screen
To understand how it all works, let us discuss the code for this service:
If you analyse this code, notice the nested structure of BOX
-es. The service is a box on the top level with two sub-boxes labeled "Login"
and "Logout"
.
The top-level box has "_SESSION"[SESSION]
as its box-term. What you must remember is that the every atom of the codomain of that term causes one contain (HTML: <div>
). In this example, the codomain of "_SESSION"[SESSION]
is just one atom, which is the session identifier. That is shown in the title of the outmost box in the browser.
<HROWS>
We would expect to see two subboxes, one labeled "Login"
and another labeled "Logout"
. However, the outmost box was annotated with <HROWS>
. The H
stands for hidden. It means that empty subboxes will be hidden from the user. Now look at the box-terms of the "Login"
- and "Logout"
subboxes. Which elements are in the codomain of the box-term of the "Login"
subbox? It says: "All sessions without an account associated with it". Since there is only one session (i.e. the browser-session) this comes down to "the current session, provided there is no account associated with it." So if nobody is logged in in the current browser session, the session atom is the only atom. Otherwise there is no atom and the "Login"
subbox is not shown. Similarly, which elements are there to make the the "Logout"
subbox appear? That box-term shows all sessions associated with an account. Which would be the account of the current session, provided someone is logged in.
That explains why the "Login"
subbox is shown when nobody is logged in and the "Logout"
subbox is shown when someone is logged in.
So let us do the following experiment: change the
<HROWS>
annotation to
<ROWS>
. Then we will see both boxes:
Notice that both subboxes have the
H
in their annotations, so in the screeshot above the
"Logout"
subbox remains empty. However, when logged in, the other subbox remains empty:
Services are meant to expose functionality and data from a context, to let users or information systems interact with the system by creating, reading, updating, and deleting data.
Note: The service definition must be outside a pattern
The following figure is an example of a user interface, which shows the name, status, e-mail and co-workers of a person called "J. Lovell".
The specification of this user interface is given in the following service definition:
To understand this fragment, take notice of:
The name of this service is Person
. This name immediately follows the keyword INTERFACE
.
The term following the colon, I[Person]
, is the interface term of this service.
The service can be applied to any atom from the domain of the interface term. So this particular service is applicable to any atom of type Person
. In the screenshot, it applies to "J. Lovell"
.
The labels "Name", "Status", "Email", and "Works with" correspond to field names in the user interface.
Each term at the right of a field name specifies which data is presented in the field. For this reason it is called the field term for that field. Field name and field term are separated by a colon.
Of all pairs <"J. Lovell", x>
from the field term, the field displays the right atom x
. A field term always works on one specific atom on the left, which is "J. Lovell"
in this example.
Field terms are subject to type checking. The following relations provide an example for getting a type-correct service:
The source concepts of a field term must match the target concept of the interface term.
Looking at the screenshot, we can tell that "J. Lovell"
has one personName (which is "J. Lovell"
), it has no personStatus, one personEmail and three persons to work with in RELATION workswith
.
You can create structure in a service by nesting. Here is an example:
The specification of this service is given in the following code fragment.
Notice the following features:
1. The structure of a service is hierarchical. It consists of boxes within a box. This is because a field term may be followed by a BOX
with a list of subservices. Without it, it is just a field term. 2. When a field term is followed by a BOX
, every atom in the codomain of the field term is displayed in a box of its own on the screen. That box behaves like a service with the field term serving as interface term of that subservice. 3. By this mechanism, the hierarchical structure of the entire service translates directly to the hierarchical structure of the web-page in which it is displayed. 4. The source concept of a field term must match with the target concept of the field term outside the box.
5. The target concept of a field term that has a box, must match with the source concepts of each field inside that box.
Especially in more complicated services, you will find it nice to adapt the layout of the fields in the user interface. For this purpose, you can substitute the word BOX
by COLS
, ROWS
, or TABS
, as in the following code fragment. Note that these annotation have no meaning other than to change what the user interface looks like.
Notice the effect that these changes have on the user interface.
Notice the following features:
1. The keyword BOX <TABS>
turns the box into a layout with tabs.
2. The keyword BOX <TABLE>
turns the layout 90 degrees into columns.
3. The keyword BOX <FORM>
is default for any box. It does not change the effect of BOX
.
Compile and run the script Project Administration Example. Start by reproducing everything that is shown above. It is quite likely that you will be trying out your own ideas before you get to the end... Have fun!
After finishing your assignment, you have learned:
to explain how a service definition is displayed on the screen of a user.
to predict which data items a service applies to, if you know which pairs are in an interface term.
to predict which data items are displayed, if you know which pairs are in a field term.
to explain which atoms are used in a sub-interface.
to understand what the keywords TABS
, COLS
, and ROWS
do to your display.
More than one service may apply to the same atom. That gives you a choice on runtime to which service you want to navigate. If no service applies, that atom is not navigable.
<rule>
a statement that declares a rule
<classify>
a statement that specifies generalization/specialization of concepts
<relation>
a declaration of a relation, stating the existence of a relation within the context
<conceptDef>
a description of a concept, to document its meaning
<representation>
a statement that defines the atomic type of a concept
<roleRule>
a statement that makes a role responsible for satisfying a rule
<ident>
a rule that defines an identity on a concept
<viewDef>
a statement for presenting facts in a readable sentence
<purpose>
a statement to describe the purpose of a pattern or a pattern element
<population>
a statement that sums up the initial population of a relation
You don't have to put up with the Ampersand built-in layout options if they don't suit your purpose. You can change most anything by including your own code snippets. (to be done...).
The purpose of this statement is to automatically modify the population of a relation based on rules.
Since ampersand 4.4.0 the syntax of this statement is:
The <operator> can be one of :=
, :<
or >:
.
This statement may occur anywhere within a context, either inside or outside a pattern.
This statement means the population of the relation will automatically be kept respectively equal ( :=
), a subset (:<
) or a superset (>:
) of the population of the given term.
Both the sources and the targets of the relation and the term must match. An error message is given otherwise.
The relation must be specified in order to use it here, as is the case with any relation used in an expression.
To store data in a database corresponds to populating the relations in a context. Atoms are the data and pairs of atoms are inserted and deleted during the lifetime of a relation.
All pairs in a relation are called the population of that relation. All atoms in a concept constitute the population of that concept. The population of all relations and concepts in a context make the population of that context.
There are two ways to populate a concept with atoms:
A POPULATION
statement defines the initial population of a concept or a relation.
An INCLUDE
statement defines the initial population from an xlsx-file (i.e. an Excel speadsheet)
Using spreadsheets to define an initial population allows you to work with larger populations. Often you can use an existing spreadsheet and adapt it to become acceptable as Ampersand input.
You can define atoms separately and you can define the pairs in a relation. Both methods result in added population for each concept.
The list of pairs is a comma-separated list between square brackets. Pairs are comma-separated pairs between round brackets. Each atom is enclosed in double quotes.
Ampersand is meant for back-end design. It offers no features for front-end design. For that purpose we advise you use contemporary front-end tools for web-based applications. Your Ampersand application is designed to be adaptable, especially for this purpose.
However, Ampersand offers a few layout features that let you place items. It has three built-in layout options, colums, rows and tabs, which you can mix freely.
The column layout uses BOX <TABLE>
to instruct the front-end application to use a tabular layout in user interfaces. Here is an example of a service, which uses the table layout.
This service shows three columns in the user interface, Students, Course and Modules. The first column is not readable, because the CRUD annotation blocks this column for reading. It would have shown students in each row, because the target of V[SESSION*Student]
is Student
. The second column shows courses in two columns, Course and Modules. The third column shows modules in three columns. This is what the user will see on the screen.
The row layout uses BOX <FORM>
to instruct the front-end application to layout the user interface row by row. Here is an example of a service, which uses the row layout on the top level.
This service shows three rows in the user interface, Students, Course and Modules. The first column shows students in each of its rows. Each student is shown in the column layout. The second row shows courses in two columns, Course and Modules. Please read about templates if you are curious which other ways of displaying information there are besides BOX <FORM>
. Please read the explanation of CRUD annotations if you are curious about the CRUD annotations. This is what the user will see on the screen.
The column layout uses BOX <TABS>
to instruct the front-end application to tabs in the user interface. Here is an example of a service, which uses the column layout.
This service shows three tabs in the user interface, Students, Course and Modules. Only one tab is shown at a time, to avoid cluttered data. This is what the user will see on the screen.
We have discussed the COLS
, ROWS
, and TABS
layout options. Please note that these options do not change the semantics; whatever your options, Ampersand displays the same data in the same fields.
If these options are not enough, you can enhance your application with your own layouts.
In this section we will make an Ampersand script that is based on an existing spreadsheet. This technique is useful for quickly adding population to an information system. Ampersand has a facility that allows you to import existing .xlsx files with minimal changes.
We can consider Ampersand as a finite system of relations. Every relation is a set of (ordered) pairs and each pair contains two atoms. However, in the real world we also store information in wider tables, as we do in spreadsheets and relational databases. Here is the trick. If we have two pairs that share the same left atom, e.g. (1, Abraham) and (1, Lincoln), we can put them in the same row. Using the same trick, we can interpret a row in a spreadsheet as a number of pairs.
Let us look at an example:
Since Ampersand works with relations, it must represent this table as relations. Three relations can do the job in the following manner:
Notice that the column names in the table correspond with the relation names in Ampersand. In the table we call them "attributes". So it makes sense to say that a relation in Ampersand can correspond with an attribute in a table.
In theory, the population of the Hawaii-script might just as well be given in a spreadsheet. This works in practice too. It looks like this:
Please copy this in a spreadsheet of your own. The element in the first column with square brackets tells Ampersand that a new table starts. The first row contains relation names. The second row contains concept names. The rows that follow contain pairs. Ampersand reconstructs those pairs as in the example above.
In practical applications, you might want to reuse data from existing spreadsheets. People tend to have lots of "informal administration" in spreadsheets, which gives you access to authentic population. Surely you need that data organized in rows, but fortunately that is reasonably common. In such cases, you just add two lines above each table to inform Ampersand about the relations that are populated. In other cases, you have some work organizing the spreadsheet for importing it.
You will find the Excel import function in the menu bar on the top right of your screen
.
This is what your upload screen looks like:
You can upload one or more .xlsx-files by dropping them in the drop zone or by selecting them. You have to upload the population with the green
Upload
button. At that time, all population from the .xlsx-file is added to the context and checked for inconsistencies. As a result, you may get errors when uploading. Only error-free spreadsheets will be uploaded successfully. As long as an error remains, the population in your context will not change.
Make a population of your own for the Hawaii-script and put it in a .xlsx spreadsheet. As described above. Make sure to delete the population statements from your Hawaii source code, to make sure that you get to see the population from your .xlsx-file. Generate a prototype from your Hawaii-application, upload your population in Excel and play around with the results.
After finishing your assignment, you have learned:
to upload population to your Ampersand application in the form of a spreadsheet in .xlsx-format;
to understand how a POPULATION
-statement relates to the contents of a spreadsheet;
that the contents of the spreadsheet is added to the population of your context, provided this does not lead to any conflict.
firstname
lastname
birth
1
Abraham
Lincoln
February 12, 1809
2
Barack
Obama
August 4, 1961
3
Calvin
Coolidge
July 4, 1872
4
Dwight
Eisenhower
October 14, 1890
[Subject]
pass
required
Subject
Student
Destination
Surfing
Brown
Hawaii
Surfing
Conway
Latin
Brown
Rome
World Religions
Applegate
World Religions
Brown
Rome
This statement is a rule, which defines an identity on a concept. It is syntactic sugar for specifying a set of relations that identify atoms in a specific concept. For example, if relations pi
and rho
determine an atom of concept T
uniquely, you can write:
As the IDENT statement defines a rule, it can be in the same places as any other RULE.
where:
<label>
is the name of the rule. It can be a single word or a string (enclosed by double brackets). It is followed by a colon (:
) to distinguish the label from the concept that follows.
<Concept>
is the name of the Concept for atoms of which the rule specifies an identity
Between brackets are terms whose source concept must be <Concept>
. This is enforced by the type system.
translates into the following rule:
Note that
in case everye
is both univalent and total, e<>e~
equals e;e~
, and the rule is equivalent to:
in case every e
is univalent but not total, you should use the IDENT
statement (or the rule that it implements), because that also works when an e
is not populated.
How do you get the moment your script has started to run?
The runtime system of Ampersand contains a function that produces the current date. Here is an example how to use it:
If you run this program, this is what you'll see
The rule "Initialize today's date" tells us that there must be a date for every session. When your session starts, there is a session atom: _SESSION
. The relation sessionToday
does not relate that session atom to a date, so the rule is violated. As a consequence, the ExecEngine triggers the violation and calls the function SetToday
. That PHP-function creates the desired link in the relation sessionToday
. That is then displayed in the user screen.
In order to enable multiple configurations of some functionality, Ampersand allows developers to specify code fragments that are conditionally ignored (or included, depending on your point of view). This allows you e.g. to include code that you need for debugging/development, but that is not needed in demonstrations. It also allows you to create modules, e.g. SIAM, that developers can re-use yet have control over the functionality that is included.
The preprocessor allows for
setting or clearing preprocessor variables. This is done by means of an extension of the INCLUDE
statement.
testing a preprocessor variable, and including or ignoring a subsequent text depending on the value of that variable.
The preprocessor syntax has been designed in such a way that scripts that use the preprocessor syntax can also be compiled by versions of Ampersand that do not support preprocessor syntax. In such cases, the preprocessor syntax is treated as comment (and hence ignored). Obviously, compiling scripts whose consistency relies on preprocessor statements cannot be expected to compile with versions of Ampersand that do not support the preprocessor syntax.
Here is an example of how preprocessor variables can be used within a script. In this example, a list of usernames and passwords is shown when the preprocessor variable Developing
is true. If that variable is false, all texts between the --#IF
and --#ENDIF
are treated as comments (ignored)
Preprocessor variables are assigned values in INCLUDE
statements, e.g. as follows:
Variables are set or cleared by specifying their names (in quotes) in a list following the --#
preprocessor directive. When a variable name is preceeded with a !
-character, its value is cleared (set to 'false'). Otherwise, its value is set (to 'true').
At the time of writing, the preprocssor specifications are as follows:
A line of text, that (a) starts with optional whitespace, (b) is followed by 2 or more -
characters, (nu is dat 2 -
chars) (c) is followed by optional whitespace, (dat mag nu nog niet) (d) is followed by #
and (e) is followed with optional whitespace, is not interpreted by the Ampersand parser, but is passed to the Preprocessor instead. The text behind the #
-character up till the end of the line is the TextToBePreprocessed
. TextToBePreprocessed
is defined by the (PCRE) regex ^\s*--+\s*#\s*(?P<TextToBePreprocessed>.*)$
. Note that Ampersand versions that do not support preprocessing will treat such texts as comment.
A preprocessor Keyword is the first word in TextToBePreprocessed
. Keyword
is defined by the (PCRE) regex (?P<Keyword>\w+\b)
when it is applied to TextToBePreprocessed
. In absolute terms, that would be ^\s*--+\s*#\s*(?P<Keyword>\w+\b).*$
followed by optional whitespace and keyword consisting of alphanumeric characters. Keywords are (thus) case sensitive. Examples: --#IF Debugging
or -- # ENDIF
Currently, valid keyword syntax is as follows (the (PCRE) regexes are assumed to be applied on TextToBePreprocessed
):
IF
, IFNOT
each take one argument - a variable.
Formally, this is defined by (PCRE) regex (?P<Keyword>IF|IFNOT)\s+(?P<Variable>\w+)
.
Examples --#IF Debugging
or --#IFNOT UserSpecifiesLoginMethod
ELSE
and ENDIF
do not take arguments.
The preprocessor treats any text following these syntaxes as comments (i.e.: ignores such texts)
Also the syntax of INCLUDE
statements is extended to include an optional comment that specifies a list of quoted variable names. Example INCLUDE "../SIAMv3/Login.ifc" --# [ "Debugging", "NoLogout" ]
. The (PCRE) regex is \bINCLUDE\s+"(?P<fileid>[^"]+)"(\s+(--#\s*)?\[\s*"(?P<var1>!?\w+)"(\s*,\s*"(?P<var2>!?\w+)")*\])?
where any text in groups var1
or var2
are variable names that may (optionally) be preceeded with a !
character. For each such variable names, a variable is created that can be referenced by its name. When the variable name was preceeded with a !
character, its value is initialized as 'false'; When the variable name was not preceeded with a !
character, its value is initialized as 'true'; If a variable with a specified name was already created, the newly created variable takes precedence. After a file inclusion terminates, the variables that the INCLUDE statement created are all destroyed.
When the preprocessor parses the file that is INCLUDE
d, preprocessor commands that evaluate a variable (such as IF
or IFNOT
) will use the value as defined in that INCLUDE
statement or (recursively) in that of a 'higher' INCLUDE
statement.
To generate documentation, Ampersand is language aware.
Ampersand assigns a language to every text written as documentation, whether it is a MEANING
, PURPOSE
or other text except comment.
Ampersand does not recognize any language, so you must tell which language is meant. To tell Ampersand what language you use, you can append a language directive to a context, a meaning, and to a purpose statement. Currently English and Dutch are supported.
A language directive has the following syntax
Where <language>
can be ENGLISH
or DUTCH
.
The first example is a context declaration in which the language ENGLISH
is specified.
This means that all natural language elements within this context are written in ENGLISH
, unless specified otherwise.
The second example is a MEANING
, which can be used in a RULE
statement and in a RELATION
statement. This example uses a MEANING
in ENGLISH
:
The language directive IN ENGLISH
means that the meaning of the relation ptpic[Pattern*Image]
is written in ENGLISH
.
The third example is a PURPOSE
statement in which the language DUTCH
is specified.
This means that the contents of this purpose statement is written in DUTCH
.
Ampersand assumes that whatever is written is written in the language denoted in the language directive. It doesn't check whether that language is actually used, because it cannot recognize languages.
If a CONTEXT
has no language directive, IN ENGLISH
is used by default. If a CONTEXT
has a language directive, that language will be the default language of all natural language items within that context.
If a PURPOSE
statement or a MEANING
has no language directive, Ampersand assumes this to be the language of its context. So, the user needs to specify a language only if it is an exception to the default.
Documentation generated by the Ampersand-compiler is written in a single language, which is specified when the compiler is called.
Documentation generated by RAP3 is written in DUTCH
. Natural language items written in any other language are ignored. This is , but a feature. RAP3 only "speaks Dutch" and ignores anything else.
This statement will be available in Ampersand vs. 4.
This statement provides syntactic sugar for defining tabular information conveniently. It introduces a number of relations and rules in a single statement, to simplify a script.
where:
<label>
is the name of the rule. It can be a single word or a string (enclosed by double brackets). It is followed by a colon (:
) to distinguish the label from the concept that follows.
<Concept>
is the name of the Concept for atoms of which the rule specifies an identity
Between brackets are terms whose source concept must be <Concept>
. This is enforced by the type system.
translates into the following declarations:
Multiplicity annotations are allowed. For example:
translates into the following declarations:
This statement makes nice combinations with the IDENT statement. For example to define two identities for persons:
This states that a person is uniquely defined by ssn
, but also by the combination of name
, birthplace
, and birthdate
. This statement can also be used to objectify (reify) an term e
If a user is tempted to replace the Create/Delete pair with a single equivalence, this becomes:
The design considerations of Ampersand are treated as principles, not as laws. In case of conflicting design considerations, choices have been made.
An Ampersand context presents itself as a universe with constraints. This was chosen to allow for incremental development. Adding a constraint changes nothing to the semantics of other constraints.
One example of this is found in the CRUD annotations: A service provides all possible CRUD-functions, which are constrained by CRUD annotations.
The Ampersand engineer gets as much freedom as possible, to facilitate incremental development. Every obligation imposed on the Ampersand-engineer is a necessity.
One example is the freedom to define PURPOSE
statements. Although the Ampersand team feels strongly about the need to specify a purpose for almost everything, the choice is left to the user. Since there is no hard technical reason for having them, the compiler does not impose its use.
As a consequence, the Ampersand engineer can choose the order in which she adds statements. This is consistent with the order-free semantics of Ampersand.
The semantics of a script is well-defined and self-contained, to ensure predictable behaviour of information systems defined in Ampersand.
The prime example is the relation algebraic semantics of terms, which is defined unambiguously in a well-studied formalism that is over a century of age.
Tools must be reliable and may not distract their users from their real work: designing information systems. For this reason we have paid much attention to making reliable tools and to automate the process of designing and building.
An example is the generation of software by the Ampersand compiler, which makes Ampersand more than a design tool. But also the deployment automation with Docker exemplifies this principle.
Every Ampersand script, that passes the compiler without errors yields a working system. This makes it easy to focus on working systems, because partial implementations can be demonstrated and tried. (prototyping).
This principle makes it easy to neglect documentation.
CRUD annotations are used in services to constrain the functionality of fields and boxes in an INTERFACE
-statement. This allows you to minimize the functionality for your users, to design for easy learning.
Each CRUD annotation comes right after a term, so we can always refer to "the term" to which a CRUD annotation belongs. A CRUD annotation constrains the things your user can do with the target atoms and the pairs of its term.
The CRUD-annotation specifies Create, Read, Update, and Delete rights for the term it follows. Capital = allowed, Non-capital = not allowed. CRUD is the default, so if you specify nothing, everything is allowed. The following service definition illustrates this.
The user interface defined by this service is shown as a screenshot below. Notice that the lowercase r in the annotation of the Students box prevents showing the underlying box. The full CRUD functionality in Course yields 'create' functionality (the green plus-button), 'remove pair' functionality (red minus button), and 'delete atom' functionality (the red trash can button). The lowercase c, u, and d in the Modules box prevents displaying that functionality in the user interface.
The next sections give some more detailed information on the run time semantics for CRUD annotations as implemented in Ampersand.
A top-level Update or Create are common in my own scripts, e.g. to create an overview of People and be able to create a new Person: INTERFACE "People" : V[SESSION*Person] CRud COLS []
. And update is also possible.
The red minus is enabled by U
. It unlinks an atom (by deleting a pair from a relation) and leaves the atom alone.
The red trash bin is enabled by D
. It removes an atom and all pairs in which that atom is used.
Motivations for CRUD-functionality are found in the GitHub discussions on CRUD functionality.
Read
CRUD for boxes
CRUD for fields
R
Read is allowed
Read is allowed
r
Read is not allowed
Read is not allowed
CRUD
for a box
for a field.
C
c
Atoms cannot be created
Atoms cannot be created
Update
CRUD for boxes
CRUD for fields
U
u
Update is not allowed
Update is not allowed
Delete
CRUD for boxes
CRUD for fields
D
d
delete not allowed
delete not allowed
A + (plus) button is displayed that lets you create a new atom, but only if the box-expression is editable.
Enter a new atom and a +
button appears. Click the + to add that atom to the listed set of atoms. If you enter an atom that exists (Peter), you can select it.
Removing and/or adding a pair (src,tgt) is allowed if expr is editable and the atom exists. Deleting a pair is done with the - button; the atom will NOT be deleted.
Removing and/or adding a pair (src,tgt) is allowed if expr is editable and the atom exists. Deleting a pair is done with the - button; the atom will NOT be deleted.
Deleting a pair is done with the - (minus) button. Deleting an atom is done with the trash bin.
Delete atom (tgt) and all pairs in which it is used.