8. Stencils — the signature feature
A stencil is what makes STencil unique: a first-class, reusable data template
with computed fields. You define a shape once, then "stamp" it with ! to produce
records. Think of a real stencil: a cut-out pattern you press onto material.
Defining a stencil
stencil User(name, age) {
name: name,
age: age,
adult: age >= 18, // computed field
tag: name + "#" + age // fields can use parameters
}
Fields are evaluated top to bottom, and a later field may use an earlier one.
Stamping with !
Name!(args) stamps the template and returns a record (object). Read fields with .:
let u = User!("Ember", 20);
print(u.name); // Ember
print(u.adult); // true
print(u.tag); // Ember#20
Reshaping data
A stencil whose parameter is existing data transforms it into a new shape:
stencil Summary(u) {
who: u.name,
status: u.adult ? "adult" : "minor"
}
let s = Summary!(u);
print(s.who, s.status); // Ember adult
Stamping over a collection: !*
Name!*list stamps every element and returns a new list — like a built-in map:
let people = [User!("Ada", 30), User!("Ben", 12), User!("Cem", 17)];
let summaries = Summary!*people;
for x in summaries {
print(x.who, "=>", x.status);
}
// Ada => adult
// Ben => minor
// Cem => minor
Why stencils?
- Declarative: describe the shape and derived values, not step-by-step construction.
- Reusable: one template, stamp it anywhere.
- Great for building/transforming data: API responses, records, config, game entities.
// game entities as stencils
stencil Monster(name, hp, power) {
name: name, hp: hp, power: power,
label: name + " (HP:" + hp + ")"
}
let dungeon = [Monster!("Goblin", 12, 3), Monster!("Dragon", 30, 9)];
for m in dungeon { print(m.label); }
Next: Error Handling
Kitteniverse Studios