Effects and capabilities
HollowScript has no ambient authority. A program cannot print, read the clock, generate a random number, read a file, or reach the network unless the host running it has granted that ability, and the file itself has declared with uses that it wants it. Reading the first few lines of a file tells you everything it could possibly touch outside its own computation, there is no way to reach further capabilities from inside ordinary code. See Chapter 9.
- A capability is a binding the host supplies: a single function (
print) or a namespace of functions (clock, withnow,todayand more). - A grant is the host’s decision to make a capability available, sometimes scoped (
fs:read=./dataonly allows reading under./data). - Every capability function has an effectful type, and every call to one is marked with the
effectkeyword, whether it reads, writes, or merely observes. The marker is required on every such call and forbidden everywhere else, there is no way to call a capability by accident, and no way to hide that a piece of code does.
uses print, clock
fn apply(f: effect fn(String), text: String) { effect f(text)}
constant stamp = effect clock.now()
effect print("timestamp length: ${stamp.length()}")
apply(print, "capabilities flow through parameters")Output:
timestamp length: 20capabilities flow through parametersclock.now() gives the current instant as YYYY-MM-DDTHH:MM:SSZ, which is always exactly 20 characters, so this example is exact and repeatable despite reading the clock. apply takes print (or any effectful fn(String)) as an ordinary parameter and calls it with effect. A pure function type is assignable to the matching effectful one, so a plain closure can be passed wherever an effectful function is expected, but never the reverse: a function that only accepts fn(String) cannot be handed print at all, the type system stops that at compile time.
Why a host grants them
Section titled “Why a host grants them”The three capabilities every host grants by default, print, clock and random, cannot read or change anything the person running the script needs to protect: print writes only to the process’s own output, clock only reveals the time, and random only returns numbers the host itself produced. Anything with more reach, reading files, using the network, reading environment variables or arguments, has to be granted explicitly and is off by default. See section 9.5 for scoped grants such as fs:read=./data and http=api.example.com, and Chapter 16 for what each standard capability does.
uses lines are not inherited through import: every module that wants a capability has to declare it for itself, even one it only uses via an imported function. Grants belong to a whole run, not to a single module, which is why the code that decides which modules get loaded is itself part of the trust boundary. See section 9.2.