Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Asking for data

A large program, such as a bank, often wants one field from many records, the total of those amounts, how many records there are, their average, or the records in order. get, sum, count, average, and sorted by ask for that data in one line. for is still there when each record needs its own step. One record stays a record. A list wraps several records, and a record wraps a list when one thing owns many items. When a value wraps another shows each case.

get and sum are the usual choice for reading and totaling. You name the field. Quill walks the list. A missing amount is left out. for is the choice when this row is confirmed and the next row is declined, because each row does different work.

The total

sum adds one field of every record in a list:

sisters = record name = "Nchang Sisters", fee = 80000 USD
brass = record name = "Bamenda Brass", fee = 150000 USD
roster = list sisters, brass

say sum fee of roster
230000 USD

Read it as: “sum the fee of the roster.” Each record has fee. Quill adds the amounts. You do not write the adding.

The same line reads one record. The sum is that one amount:

school = record name = "Harvard", fees = 10000

say sum fees of school
10000

When an amount might be missing, sum adds the ones that are there:

fn fee_for(name: text) -> maybe USD
  when
    name == "Quiet Hour" -> return missing
    else -> return found 40000 USD

quiet = record name = "Quiet Hour", fee = fee_for("Quiet Hour")
drums = record name = "Market Drums", fee = fee_for("Market Drums")
roster = list quiet, drums

say sum fee of roster
40000 USD

Quiet Hour has no fee, so it is left out. The till is the money that exists. If every amount is missing, the sum is 0 in that currency.

sum only adds a number or an amount of money:

sisters = record name = "Nchang Sisters", fee = 80000 USD
roster = list sisters
say sum name of roster
Problem on line 3 of example.quill:

    say sum name of roster
            ^^^^
sum adds amounts, but name is Text.
Name a number or an amount of money: sum fee of roster.

One field from every record

get takes one field and gives you a list:

sisters = record name = "Nchang Sisters", fee = 80000 USD
brass = record name = "Bamenda Brass", fee = 150000 USD
roster = list sisters, brass

say get fee of roster
80000 USD, 150000 USD

A missing amount is left out, the same way sum leaves it out. The list holds the fees that exist.

Several fields

Name every field you need, separated by commas. get gives a list of records that keep only those fields, in the order you wrote them:

sisters = record name = "Nchang Sisters", age = 24, fee = 80000 USD
brass = record name = "Bamenda Brass", age = 31, fee = 150000 USD
roster = list sisters, brass

people = get name, age, fee of roster
person = people at 1
say person.name
say person.age
say person.fee
Nchang Sisters
24
80000 USD

person is the first record. person.name reads its name. (people at 1).name is the same value. The parentheses are there because a dot binds to the value just before it, and that value would be the number 1.

The first record in people has name, age, and fee. It does not have any other field the original record had.

The same line reads one record. The result is one record with only the fields you named:

school = record
  name = "Harvard"
  location = "United Kingdom"
  year = 2026
  fees = 10000

data = get name, location, year, fees of school
say data.name
say data.fees
Harvard
10000

Name each field once:

sisters = record name = "Nchang Sisters", fee = 80000 USD
roster = list sisters
say get name, name of roster
Problem on line 3 of example.quill:

    say get name, name of roster
                  ^^^^
name is already in this get.
Name each field once: get name, fee of roster.

A field further in

The value after of can be a field of another record. Write the path.

school = record
  name = "Harvard"
  acceptances = record gpa = 4.0, sat = 1500

say get gpa of school.acceptances
4.0

school.acceptances is one record, so this is that one number.

A list can sit on a record the same way. The place and the acts are already on each booking:

up = record place = "Up Station", acts = list "Bamenda Brass", "Hilltop Duo"
old = record place = "Old Town", acts = list "Market Drums"
desk = record bookings = list up, old

say get place of desk.bookings

first = desk.bookings at 1
say first.place
say first.acts
Up Station, Old Town
Up Station
Bamenda Brass, Hilltop Duo

desk.bookings is the list. get place of desk.bookings is every place. first is the first booking, so first.place and first.acts need no parentheses.

Records inside other records

with and in name the list and the field embedded in it, starting from the record that holds the list. The acts come back with the bookings:

up = record place = "Up Station", acts = list "Bamenda Brass", "Hilltop Duo"
old = record place = "Old Town", acts = list "Market Drums"
roster = record bookings = list up, old

tonight = get bookings with acts in roster
first = tonight at 1
say first.place
Up Station

bookings is the list you want. acts is the data embedded in each booking. in roster is where those records live. tonight is the bookings, and each one still has its acts.

The same line works when the bookings are inside a list of records. Quill gathers them into one list:

up = record place = "Up Station", acts = list "Bamenda Brass"
old = record place = "Old Town", acts = list "Market Drums"
saturday = record day = "Saturday", bookings = list up
sunday = record day = "Sunday", bookings = list old
nights = list saturday, sunday

all = get bookings with acts in nights
second = all at 2
say second.place
Old Town

The embedded field has to be there:

up = record place = "Up Station", acts = list "Bamenda Brass"
roster = record bookings = list up
say get bookings with guest in roster
Problem on line 3 of example.quill:

    say get bookings with guest in roster
                          ^^^^^
This record has no field called guest.
Its fields are place, acts.

Only some records: where

Often you want only some of the records: the students who passed, the fees that are not paid yet. where keeps the records whose condition is true, in the same order:

amina = record name = "Amina", score = 80, fee = 500 USD, paid = true
omar = record name = "Omar", score = 64, fee = 300 USD, paid = false
john = record name = "John", score = 45, fee = 200 USD, paid = false
students = list amina, omar, john

passed = students where score >= 50
say get name of passed
Amina, Omar

Read it as: “the students where the score is 50 or more.” Inside the condition, score is the field of each record. You don’t write student.score, and you don’t write the loop.

where works right inside get and sum, so one line can pick the records and ask for their data:

amina = record name = "Amina", score = 80, fee = 500 USD, paid = true
omar = record name = "Omar", score = 64, fee = 300 USD, paid = false
john = record name = "John", score = 45, fee = 200 USD, paid = false
students = list amina, omar, john

say get name of students where score >= 50
say sum fee of students where paid == false
Amina, Omar
500 USD

Join conditions with and and or, or write where again. Both lines below keep only Omar:

amina = record name = "Amina", score = 80, fee = 500 USD, paid = true
omar = record name = "Omar", score = 64, fee = 300 USD, paid = false
john = record name = "John", score = 45, fee = 200 USD, paid = false
students = list amina, omar, john

say get name of students where score >= 50 and paid == false
say get name of students where score >= 50 where paid == false
Omar
Omar

The text tools work in a condition too, so where can look inside text:

amina = record name = "Amina Okafor", email = "amina@school.org"
omar = record name = "Omar Diallo", email = "omar@mail.com"
students = list amina, omar

say get name of students where email ends with "school.org"
say get name of students where name contains "Omar"
Amina Okafor
Omar Diallo

The condition can read values from outside, too. A field comes first: if the records have a score, then score in the condition is the record’s score.

amina = record name = "Amina", score = 80
omar = record name = "Omar", score = 64
students = list amina, omar

pass_mark = 70
say get name of students where score >= pass_mark
Amina

When no record matches, the answer is an empty list, not a problem:

amina = record name = "Amina", score = 80
students = list amina

say students where score > 100
an empty list

for can walk the records that match: for s in students where paid == false.

How many: count

count of says how many items a list has:

amina = record name = "Amina", score = 80, fee = 500 USD, paid = true
omar = record name = "Omar", score = 64, fee = 300 USD, paid = false
john = record name = "John", score = 45, fee = 200 USD, paid = false
students = list amina, omar, john

say count of students
say count of students where score >= 50
say count of students where paid == false
3
2
2

Read it as: “the count of the students where the score is 50 or more.” With where, only the records that match are counted. You don’t keep a counter and add 1 to it in a loop.

A count is a number, so it fits wherever a number does: inside text, in a when, or in a calculation:

amina = record name = "Amina", score = 80
omar = record name = "Omar", score = 64
students = list amina, omar

say "{count of students} students"
when
  count of students >= 30 -> say "The class is full."
  else -> say "There is room for {30 - count of students} more."
2 students
There is room for 28 more.

count is a Quill word only right before of. A value of your own can still be called count:

var count = 0
set count = count + 1
say count
1

The average

average adds one field of every record and divides by how many there are:

amina = record name = "Amina", score = 80, fee = 500 USD, paid = true
omar = record name = "Omar", score = 64, fee = 300 USD, paid = false
john = record name = "John", score = 45, fee = 200 USD, paid = false
students = list amina, omar, john

say average score of students
say average fee of students
say average score of students where score >= 50
63
333.33 USD
72

The fees add up to 1000 USD, and a third of that is 333.333… USD. Money is rounded to whole cents, half to even, the way round total to cents rounds it. A number keeps every digit. To show fewer, round it: round average score of students to 1 place.

A missing amount is left out, the same way sum leaves it out. The average is of the amounts that are there.

An average of nothing has no answer, so Quill stops and says so:

amina = record name = "Amina", score = 80
students = list amina

say average score of students where score > 90
Problem on line 4 of example.quill:

    say average score of students where score > 90
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There is nothing to average: students where score > 90 has no score.
An average needs at least one. Check count of students where score > 90 first, in a when.

When the list might be empty, check its count first:

amina = record name = "Amina", score = 80
students = list amina

top = students where score > 90
when
  count of top > 0 -> say average score of top
  else -> say "No one scored above 90."
No one scored above 90.

Like count, average is a Quill word only when a field and of come after it. fn average(total: number, many: number) is still a function you can write.

In order: sorted by

sorted by puts the records of a list in order of one field. The lowest comes first:

amina = record name = "Amina", score = 80, fee = 500 USD, paid = true
omar = record name = "Omar", score = 64, fee = 300 USD, paid = false
john = record name = "John", score = 45, fee = 200 USD, paid = false
students = list amina, omar, john

say get name of students sorted by score
say get name of students sorted by highest score
say get name of students sorted by name
John, Omar, Amina
Amina, Omar, John
Amina, John, Omar

sorted by highest score puts the highest first. sorted by lowest score is the same as sorted by score, for when you want the line to say it. Numbers and money go from smallest to largest. Text goes in alphabetical order, with capital and small letters together. Two records with the same value keep the order they had.

sorted by goes after where, so one line can pick the records and put them in order:

amina = record name = "Amina", score = 80, fee = 500 USD, paid = true
omar = record name = "Omar", score = 64, fee = 300 USD, paid = false
john = record name = "John", score = 45, fee = 200 USD, paid = false
students = list amina, omar, john

for student in students where paid == false sorted by highest fee
  say "{student.name} owes {student.fee}"
Omar owes 300 USD
John owes 200 USD

The first record of a sorted list is the top one. Name it, then read its fields:

amina = record name = "Amina", score = 80
omar = record name = "Omar", score = 64
students = list amina, omar

ranked = students sorted by highest score
top = ranked at 1
say "Top of the class: {top.name}"
Top of the class: Amina

When each row needs its own step

for still walks every item. Use it when the work is different for each one: say the name, or decide that this booking is declined.

sisters = record name = "Nchang Sisters", fee = 80000 USD
brass = record name = "Bamenda Brass", fee = 150000 USD
roster = list sisters, brass

for act in roster
  say act.name
Nchang Sisters
Bamenda Brass

Ask with get, sum, count, or average when you want the data, the total, how many, or the typical amount. Add where to keep only some records, and sorted by to put them in order. Repeat with for when each record needs a step of its own. A program can use both.

Common mistakes

A dot binds to the value just before it. people at 1.name asks the number 1 for a field:

sisters = record name = "Nchang Sisters", age = 24, fee = 80000 USD
roster = list sisters
people = get name, age, fee of roster
say people at 1.name
Problem on line 4 of example.quill:

    say people at 1.name
                    ^^^^
1 is Number, so it has no fields.
A dot reads a field of a record, like address.city.

Give the record a name first: person = people at 1, then say person.name.

A name in a where condition has to be a field of the records, or a value you made:

amina = record name = "Amina", score = 80
students = list amina

say students where scor >= 50
Problem on line 4 of example.quill:

    say students where scor >= 50
                 ^^^^^
The records in students have no field called scor, and there's no value called scor.
Their fields are name, score.

Did you mean:  score

A where condition runs to the end of the line. In a check, name the answer first, so the comparison isn’t read as part of the condition:

amina = record name = "Amina", score = 80
omar = record name = "Omar", score = 64
students = list amina, omar

pass_total = sum score of students where score >= 50
check pass_total == 144

where keeps records, so it needs a list of records. For a list of numbers or text, use for and when:

scores = list 80, 64, 45

say scores where scores > 50
Problem on line 3 of example.quill:

    say scores where scores > 50
               ^^^^^
where keeps the records whose fields match, but scores holds Number.
Use where on a list of records, like students where score >= 50. To pick from other lists, use for and when.

sorted by names a field, so it puts records in order. A list of numbers has no fields to name:

scores = list 80, 64, 45

say scores sorted by score
Problem on line 3 of example.quill:

    say scores sorted by score
               ^^^^^^
sorted by puts records in order of one field, but scores holds Number.
Sort a list of records, like roster sorted by fee.

Keep each number on a record, like record name = "Amina", score = 80, and sort the records.

count of counts a list. One record is not a list, so there is nothing to count:

amina = record name = "Amina", score = 80

say count of amina
Problem on line 3 of example.quill:

    say count of amina
                 ^^^^^
amina is one record, and count of counts the items in a list.
count of roster is how many records roster has.

Try it

  1. Make two records with a fee in USD, put them in a list, and say the sum.
  2. get the fees into their own list and say that list.
  3. Add a record whose fee is missing, and check that the sum left it out.
  4. Put two bookings on a desk and say get place of desk.bookings.
  5. Give each record a paid field. say sum fee of roster where paid == false to see what is still owed.
  6. say count of roster where paid == false to see how many still owe, and say average fee of roster for the typical fee.
  7. say get name of roster sorted by highest fee. Then sort by name and read the order.