Lists and dicts
list<T> is a Scratch list, with a type on the elements.
public scores: list<num> = [3, 1, 4, 1, 5];public names: list<str> = ["Ember", "Splash"];public empty: list<num> = [];Indexing
Section titled “Indexing”Scratch lists are 1-indexed, and so are Katnip’s.
temp first: num = scores[1];scores[2] = scores[2] + 1;scores[2] += 1; # compound assignment through an indexMethods
Section titled “Methods”scores.add(7); # appendscores.clear(); # delete alltemp n: num = scores.length();temp has: bool = scores.contains(4);temp at: num = scores.indexOf(5); # 0 if absentscores.show(); # show the list monitorscores.hide();Every method also has a namespace form — list.contains(scores, 4) — but it does not build
today; codegen looks for a variable named list. Use the receiver form.
Iterating
Section titled “Iterating”for (s, scores) { total += s;}dict<K, V> has no Scratch equivalent. Katnip backs each dict with two parallel Scratch
lists — for stock, they are stock_keys and stock_vals — and keeps their indices in
step.
public stock: dict<str, num> = {"apple": 2, "banana": 5};Reading and writing
Section titled “Reading and writing”temp apples: num = stock["apple"];stock["cherry"] = 7; # key missing → appendedstock["apple"] = 3; # key present → replaced in placestock["apple"] += 1;A read resolves the key column to an index, then reads the value column at that index.
Iterating
Section titled “Iterating”for ((name, count), stock) { report(name, count);}The (name, count) tuple pattern walks both columns together.
Where a collection can be declared
Section titled “Where a collection can be declared”Only at the top level or in a sprite body. A list or dict declared inside a proc or a handler is not lowered:
proc bad() -> void { temp working: list<num> = []; # NOT lowered}
public working: list<num> = []; # do this insteadScratch has no local lists either, so this is less of a restriction than it sounds — but it does mean shared scratch space is genuinely shared, including across clones and recursive calls.
How literals reach the project
Section titled “How literals reach the project”If every element is a literal, the contents are baked into the project file and are present the moment the project loads:
public scores: list<num> = [3, 1, 4, 1, 5];If any element needs a block to compute, the whole list is instead rebuilt by a green-flag script:
public roster: list<num> = [1, double(4), 9];That distinction matters when another green-flag script reads the list — Scratch does not order concurrent scripts for you. If you depend on it, broadcast after the rebuild rather than racing it.
Strings behave like sequences
Section titled “Strings behave like sequences”temp c: str = greeting[1]; # 1-based, operator_letter_oftemp n: num = len(greeting);temp has: bool = greeting.contains("at");
for (letter, greeting) { ... }There is no slicing. s[1:5:2] parses and gets a naive type, but has no lowering.
Generic helpers
Section titled “Generic helpers”temp paired: (list<str>, list<num>) = zip(names, powers);temp tagged: (list<num>, list<str>) = enumerate(names);The types infer correctly — T binds from the arguments. But zip and enumerate are
katnip_* builtins with no codegen, so they type-check and then fail the build. Walk
by index instead:
for (i, names.length()) { report(names[i], powers[i]);}