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¶
- Queries in documents: put a query in a card.
- Retrieval and graphs: rank cards and walk their links.
- The expression grammar: everything the parser accepts.