The previous lesson introduced hosted variables. They give every creation linked to an App the same set of values while keeping those values local to each host.
Suppose an App connects several lockable Items. Its Main module can define the model they all share:
host var $lockDifficulty = 10;
host mem var $attempts = 0;
host const $mastered = $attempts >= 5;
Every linked lock can now use $lockDifficulty, $attempts, and $mastered without declaring them again. The next step is giving each lock the right configuration.
Defaults
A defaults block configures the initial values of hosted variables for one linked creation. Add one at the top level of an Item or Boop’s Code module:
defaults {
$lockDifficulty = 18;
}
That Item now starts with a difficulty of 18 instead of the value declared in App Main. Another linked Item can choose a different value without changing the shared model.
Defaults can assign mutable variables in the Host scope. Constants remain calculated from their declarations.
Named defaults
Inline defaults are enough for one Item. When several locks share a configuration, give that configuration a name in App Main:
defaults trainingLock {
$lockDifficulty = 6;
}
defaults vaultLock {
$lockDifficulty = 18;
}
A linked Item can apply one by name:
defaults vaultLock;
It can also start from a named configuration and change only what makes this Item different:
defaults vaultLock {
$lockDifficulty = 20;
}
Sugar reads default applications from top to bottom, so the last assignment to a value wins. reset($lockDifficulty) restores the effective value for the current host.
Changing the default of a hosted memory variable does not replace memory that already exists. The new value is used when new memory is created or existing memory is reset.
Mixins
The locks no longer repeat their configuration, but they still need the same actions. A mixin is a named piece of Sugar source that an App can insert into linked creations. It can contain declarations, executable statements, or both.
A function is called while a program runs. A mixin is included before Sugar validates the completed creation, so it can share structure that a function cannot, including complete variables, chips, actions, texts, and functions.
Declare a mixin in App Main with mixin:
mixin lockActions(toolBonus = 0) {
action pickLock -> "Pick the lock" {
config {
description: "Try to open the lock without its key.",
consumes: 0,
}
let roll = Rand::pick(1..=20) + toolBonus;
$attempts++;
text if roll >= $lockDifficulty {
"@1 opens the lock with a roll of {roll}."
} else {
"The lock holds. @1 rolled {roll}."
}
}
action inspectLock -> "Inspect the lock" {
show variables $attempts, $mastered;
text "@1 studies the lock for weak points.";
}
}
Insert it with include at the top level of each linked Item that needs those actions:
defaults vaultLock;
include lockActions(2);
This Item receives both actions. Its lock difficulty still comes from its defaults, while the 2 gives this particular inclusion a tool bonus.
Parameters with a fallback, such as toolBonus = 0, are optional:
include lockActions;
include lockActions(3);
Required parameters must come before parameters with fallbacks. A mixin can have up to 20 parameters.
Include code where it belongs
A mixin does not have its own fixed scope. Its body is inserted exactly where you write include, then Sugar validates the resulting creation.
The previous mixin contains complete Item actions, so it belongs at the top level of an Item. A smaller mixin can contribute declarations inside an action instead:
mixin approachInput {
var approach -> "Approach": choice = "CAREFUL" with {
input: true,
required: true,
choices: ("CAREFUL" -> "Careful", "QUICK" -> "Quick"),
};
}
Use it in any action that should ask the same question:
action forceLock -> "Force the lock" {
include approachInput;
text "@1 takes a {approach} approach to the lock.";
}
The included variable belongs to forceLock, just as if its declaration had been written there directly. The same rule lets mixins contribute functions, Boop texts, Automation declarations, or executable statements wherever those forms are valid.
Mixins can include other mixins. They cannot include themselves, either directly or through a chain of other mixins.
Share Media Pools
An App may declare an App-wide List<Media> in Main. Linked Items and Boops can use that Media Pool through its normal App variable reference:
app const $rewards: List<Media> = (
&coins,
&gem,
);
A creation can derive a local pool with ordinary list operators:
const specialRewards: List<Media> = $rewards
+ (&bonus,)
- (&coins,);
Here + adds &bonus, - removes &coins, and $rewards remains unchanged.
A mixin may also contribute a complete List<Media> declaration:
mixin rewardMedia(rareAt = 80) {
const rewards: List<Media> = (
&coins,
&gem when luck >= rareAt,
);
}
Include it at the top level of a linked creation, then use the resulting list normally:
include rewardMedia(70);
action search -> "Search" {
show media from rewards;
text "@1 searches for something valuable.";
}
Pass fixed or runtime values
A fixed argument can configure either declarations or executable behavior:
include lockActions(2);
An expression that can only be known while Sugar runs is also allowed when the parameter is used by executable code:
In App Main:
mixin rollWithBonus(bonus) {
let roll = Rand::pick(1..=20) + bonus;
}
In a linked Item:
action testTools -> "Test the tools" {
include rollWithBonus(Rand::pick(1..=3));
text "@1 tests the tools and rolls {roll}.";
}
Sugar evaluates that expression once at the inclusion site. Every use of the parameter in that run receives the same result.
Runtime values cannot shape declarations that JOI must build beforehand. For example, a parameter used as the choices of an input needs a fixed list. JOI has to build that input before an action can run and call Rand::pick().
That distinction follows the same boundary as the rest of Sugar: declarations describe the creation, while executable statements do work when someone uses it.
With hosted variables, defaults, and mixins together, each linked creation can stay small:
defaults vaultLock {
$lockDifficulty = 20;
}
include lockActions(2);
The App owns the shared model and actions. The Item only says which configuration and context it needs. The next lesson introduces components and interactions that can continue an experience from a Sugar message.