Tagged Literals
● Intermediate5 minBefore this: Packages, Modules: pubSpec 10.15
A package can give text a meaning the compiler checks: sql"…" is a Query, and a misspelled keyword in it is a compile error, not a failure in production.
A package declares a tag with literal: its name, the type a literal of it makes, and the function that checks its text.
pub literal name: T check f language "x"Here the package pg declares sql. A Query's text isn't pub, so outside pg a Query comes only from a sql"…" literal:
use pg
use pg.{sql}
fn main() {
const recent = sql"select id, item from orders where owner = $1"
print(pg.text_of(recent))
const paid = pg.sql"update orders set paid = true where id = $1"
print(pg.text_of(paid))
}The program ran and printed:
kavach run
select id, item from orders where owner = $1update orders set paid = true where id = $1
Tis a record whose first field is aString. The literal's value is aTholding its text; its other fields take their defaults.- The tag is written right before the quote, with no space:
sql"…"afteruse pg.{sql}, orpg.sql"…"through the package. language "sql"is optional. It tells editors which language to colour inside the literal, as the window above does.
Checked when the program is compiled
The compiler runs the checker on each literal's text. Each problem it gives is reported at its place in the literal: an error, or a warning when its warning is true.
use pg.{sql, text_of}
fn main() {
const all = sql"select * from orders"
const recent = sql"selct id from orders where owner = $1"
const paid = sql"""
updte orders set paid = true
where day = current_date
"""
print(text_of(all))
print(text_of(recent))
print(text_of(paid))
}warning: sql: `*` gets every column, even ones added later (name the columns you need) --> src/main.kav:4:28 | 4 | const all = sql"select * from orders" | ^ error: sql: a query starts with select, insert, update or delete (is `selct` misspelled?) --> src/main.kav:5:24 | 5 | const recent = sql"selct id from orders where owner = $1" | ^^^^^ error: sql: a query starts with select, insert, update or delete (is `updte` misspelled?) --> src/main.kav:7:9 | 7 | updte orders set paid = true | ^^^^^
The checker gives a LiteralProblem for each mistake. Every program has the type; it needs no use:
/// One problem a tag's checker found (built in, every program has it).
pub type LiteralProblem {
pub at: Int // a character index into the text
pub length: Int = 1
pub message: String
pub hint: String = ""
pub warning: Bool = false
}atcounts characters of the text as written, from 0;lengthis how many the mark covers.- In a literal over lines,
atcounts the text the checker gets, with the indentation the lines share removed. The problem is shown on its own line, asupdteis above. - The checker runs while the program is compiled, so it must be pure:
fn(text: String) -> [LiteralProblem], with no!and nouses(capabilities). One that reads a file is refused. - A checker that panics or runs longer than a second is a compile error that names the tag.
The text is raw
Inside a tagged literal, \ and { are characters like any other: no escapes and no interpolation, so values can't be pasted into it. Values go in separately, as $1 does in SQL. Three quotes let a literal span lines, as other text does:
type Query {
text: String
}
literal sql: Query check check_sql
fn check_sql(text: String) -> [LiteralProblem] {
[]
}
fn main() {
const q = sql"select name from files where name = '{name}' and dir = 'C:\new'"
print(q.text)
const long = sql"""
select id, item
from orders
"""
print(long.text)
}The program ran and printed:
kavach run raw.kav
select name from files where name = '{name}' and dir = 'C:\new'select id, itemfrom orders
Text known only when the program runs
Inside its package, code may still build a T from any text. A function that does so for text known only when the program runs, such as a migration read from a file, is marked audited fn. kavach audit lists each call of it, as it lists reveal and unsafe:
use pg.{sql, text_of, unchecked}
fn main() {
const recent = sql"select id from orders where owner = $1"
print(text_of(recent))
const table = "orders_2026"
const copy = unchecked("select id from {table}")
print(text_of(copy))
}select id from orders where owner = $1 select id from orders_2026
unsafe code: nothing revealed secrets: nothing audited calls: src/main.kav:7 unchecked(…) capability use: src/main.kav:3 fn main: Console
- Generated tests (tests from types) don't make a
Tfrom made-up text. A function that takes one is tested through itsexampleclauses andtestblocks, which write literals. - Joining two literals,
sql"…" + sql"…", is not part of this version.