Skip to content

A tour of HQL

Every program on this page runs with the current release. Run one with hql eval '<program>', or ask for its type without running it with hql check - and the program on standard input.

Values

Scalars are what you would expect. Each has a type, and hql check reports it without evaluating anything.

Program Value Type
1 + 2 3 Int
1.5 + 2.25 3.75 Float
"hello" hello String
true true Bool

A program is checked as a whole before it runs, so a mistake is reported with its place and nothing is evaluated:

$ hql eval '1 + true'
type error: addition requires two Int or two Float operands
 --> <expression>:1:5
  |
1 | 1 + true
  |     ^^^^

Collections

A list keeps every element and its position. A set keeps one occurrence of each distinct value. A map pairs keys with values.

[1, 2, 3]                      // List<Int>
Set([1, 2, 2, 3])              // Set<Int>: [1, 2, 3]
Map(["robin", "owl"], [1, 2])  // Map<String, Int>

Looking up a key yields an Option, because the key may be absent:

counts = Map(["robin", "owl"], [1, 2])
counts | get("owl")            // 2, of type Option<Int>

counts | get("swallow") is none rather than an error. A record literal such as { name: "owl", count: 2 } is Data — the tree type YAML and JSON documents share.

Bindings

A line name = expression binds a name; the program's value is its last expression. Types are inferred, and an annotation states a view of the value:

xs : Collection<Int> = [1, 2, 2]
xs | count(2)                  // 2

Pipelines and lambdas

xs | f(a) means f(xs, a). A lambda, x => …, is written where a step takes a function:

[1, 2, 3] | map(x => x + 1)    // [2, 3, 4]
[3, 1, 2] | sort               // [1, 2, 3]
["a", "b", "a"] | count("a")   // 2
[1, 2, 3] | contains(2)        // true

The steps are ordinary functions, so size([1, 2, 3]) and [1, 2, 3] | size are the same program. hql builtins lists them all with their signatures.

Cards

Against a vault, cards is the set of its cards. The birds example is a small one; pass it with --vault:

hql --vault examples/birds/wiki eval 'cards | count'    # 6

A card's front matter is its metadata, and filter keeps the cards a predicate accepts. Filtering a set yields a set; sorting it yields a list:

cards
| filter(c => c.metadata.category == "bird")
| sort(by = c => c.name)
| map(c => c.title)
[Barn owl, Common raven, European robin]

The type of that program is List<String>, known before it runs.

Presenting a result

A program returns a value. How it is shown is a last, separate step — table, json or text:

cards
| filter(c => c.metadata.category == "bird")
| sort(by = c => c.name)
| map(c => c.title)
| table
value
--------------
Barn owl
Common raven
European robin

Without one, the host chooses. --format json before the command emits the type, the value and the report queue for another program to read.

Next