Enums and structs
An enum is a nominal set of named constants.
enum Fruit { apple, banana, cherry }enum Team { red = "R", blue = "B" }Implicit vs explicit values
Section titled “Implicit vs explicit values”This distinction matters more than it looks.
Implicit members — no = — fold to the qualified name:
Fruit.banana # the string "Fruit.banana"Explicit members keep their value verbatim:
Team.red # the string "R"The qualification is what makes enums nominal: two enums that both have a member called
red never compare equal, because one folds to "Fruit.red" and the other to
"Team.red".
Explicit values exist for the opposite reason — when the value has to match something Scratch already expects. Every stdlib enum that feeds a Scratch field or menu uses them:
enum StopType { ALL = "all", THIS_SCRIPT = "this script", ... }enum RotationStyle { LEFT_RIGHT = "left-right", ALL_AROUND = "all around", ... }Using enums
Section titled “Using enums”Members fold to compile-time constants, so they cost nothing at runtime:
temp pick: Fruit = Fruit.banana;
if (pick == Fruit.apple) { ... }
switch (pick) { case (Fruit.apple, Fruit.cherry) { looks.think("red-ish", 1); } default { looks.think("something else", 1); }}Comparison between two enum values is a bool, so enums work as procedure parameters and
dispatch normally:
proc effectiveness(attacker: Element, defender: Element) -> num { if (attacker == Element.WATER && defender == Element.FIRE) { return 2; } return 1;}A case label is checked against the switch value’s type, so a label that could never
match is an error rather than a dead branch:
switch (pick) { case (Team.red) { ... } # error, if pick is a Fruit}switch over an enum is still not checked for exhaustiveness. Always add a default.
Literal coercion
Section titled “Literal coercion”A literal whose value is one of an enum’s member values is assignable to that enum:
enum Team { red = "R", blue = "B" }
temp t: Team = "R"; # same as Team.redBoth forms fold to the identical value, so they emit identical blocks. This is what makes the standard library’s menu parameters pleasant to call:
pen.setAttr(pen.ColorParam.COLOR, 10);pen.setAttr("color", 10); # identical outputThe rule lives in assignability itself, so it applies everywhere a value meets a type —
arguments, initializers, assignments, return, struct field defaults, list and dict
elements, and case labels.
A literal that misses gets the member list and a did-you-mean:
"colour" is not a value of enum 'ColorParam'; must be one of:"color", "saturation", "brightness", "transparency". Did you mean "color"?Coercion matches the lowered value, which is the one qualification affects. For an enum whose members are implicit, that means the qualified string:
enum Fruit { apple, banana }
temp f: Fruit = "Fruit.apple"; # ✅ but nobody writes thistemp g: Fruit = "apple"; # error: not a value of enum 'Fruit'temp h: Fruit = Fruit.apple; # ✅ write thisSo coercion is really for enums with explicit values — which is every enum that feeds a Scratch menu. On your own implicit enums, keep using the member.
Enums declared inside a namespace
Section titled “Enums declared inside a namespace”The standard library declares enums like motion.RotationStyle and looks.GraphicEffect
inside its namespaces. Reference them through the namespace:
motion.setRotationStyle(motion.RotationStyle.LEFT_RIGHT); # ✅looks.setEffect(looks.GraphicEffect.GHOST, 50); # ✅A namespaced enum stays out of global scope — the bare name is undefined:
motion.setRotationStyle(RotationStyle.LEFT_RIGHT); # error: 'RotationStyle' is not definedThe prelude enums have no namespace, so they are bare names:
Key.SPACE # ✅StopType.ALL # ✅Structs
Section titled “Structs”A struct is a nominal record type with named, scalar fields.
struct Point { x: num, y: num }struct Cfg { a: num = 1, b: num = 2 }Construction
Section titled “Construction”Struct literals take named fields only — positional arguments are an error. Fields with defaults may be omitted:
temp p: Point = Point(x = 1, y = 2);temp c: Cfg = Cfg(a = 5); # b = 2Field access
Section titled “Field access”temp a: num = p.x;p.x = 10;Struct lists and field columns
Section titled “Struct lists and field columns”A list<Point> supports both element access and whole-column access:
temp ps: list<Point> = [];temp one: num = ps[1].x; # one element's fieldtemp col: list<num> = ps.x; # every element's x, as a list- Fields must be scalar —
list<num>as a field is rejected. - Duplicate field names are rejected.
- A field default must match the field’s type.
- Missing required fields and unknown fields are reported at construction.
- Struct-typed parameters and return types are allowed.