Source guide/programs.simd
1# Program Structure 2 3A Silo program isn't just a pile of `:fn`s. It has an entry 4point, a lifecycle, a test surface, and — distinctively — a 5**host** that decides what effects are available and drives 6execution. This chapter covers the top-level plumbing: `:main`, 7`:init` / `:deinit`, `:test`, `:cfg`, annotations, and the 8sans-IO host model that everything runs under. 9 10## `:main` — the entry point 11 12A binary's entry point is `:main`. It carries the effect row 13of the whole program — anything `:main` declares is something 14the host has to provide: 15 16```silo 17:main +Console +Env 18 .args pop-> args 19 "Hello from Silo!" print 20:end 21``` 22 23`:main` has no signature beyond its effect row. It runs, it 24returns, the program exits. The effect set it declares is 25the compact statement of what capabilities the host must 26install; everything downstream from `:main` can only use 27effects `:main` has claimed (or which come from a `:with` 28handler inside the program). 29 30A **library** has no `:main` — its entry is `src/lib.si` 31(covered in [chapter 16](./16-modules.simd)). A binary has 32both a `lib.si` and a `main.si`. 33 34## `:init` / `:deinit` 35 36An `:init` block runs once per module load, in dependency 37order, before `:main` starts. An optional `:deinit` runs 38during shutdown. Together they handle setup/teardown and — 39because `:init` can produce a value — **module-scoped 40runtime constants**. 41 42Every `:init` has a signature. The shape of that signature 43chooses one of three forms. 44 45### Named — a module-scoped constant word 46 47An `:init` with an output type and a name creates a 48module-level word that holds the computed value. The body 49runs once; the value is memoised; any call to the name 50afterwards returns the same value. 51 52```silo 53:init my-pool ( -> Pool ) +Console 54 pool-create 55:deinit +Console 56 pool-drain # Pool is on the stack 57:end 58``` 59 60After loading, `my-pool` is a word of type `( -> Pool )` you 61can call from anywhere in the module (or outside it, with 62the appropriate visibility on the `:init`). The value was 63constructed once, at load time, inside an `+Console` 64context. 65 66`:deinit` receives the produced value on the stack and is 67responsible for tearing it down — in this case, draining the 68pool before the module unloads. 69 70### Anonymous — value for `:deinit` only 71 72An `:init` without a name still produces its value, but the 73value is only reachable from its own `:deinit`. Good for 74resources that live for the module's lifetime but don't need 75to be named elsewhere: 76 77```silo 78:init ( -> Handle ) +FileSystem 79 open-something 80:deinit +FileSystem 81 close-something # Handle is on the stack 82:end 83``` 84 85### Side-effect only 86 87An `:init` with empty output `( -> )` is pure side-effect — 88no word, no stored value. Useful for registering default 89aspect handlers, installing tracing hooks, or pre-computing 90tables whose results live elsewhere: 91 92```silo 93:init setup-tracing ( -> ) +Console 94 install-trace-handlers 95:end 96``` 97 98The name is optional when the output is empty; it just gives 99the block something to show up as in diagnostics. 100 101### Order 102 103Across modules, `:init` blocks run in dependency order 104(parents before children). `:deinit` blocks run in the 105reverse order during shutdown. Within a module, multiple 106`:init` blocks run in source order. 107 108## `:test` 109 110Tests are declared at top level with `:test`: 111 112```silo 113:test "fizzbuzz of 15" 114 15 fizzbuzz "FizzBuzz" = assert 115:end 116``` 117 118A test has a name (a `Str` literal), an optional effect row, 119and a body. The body runs under whatever effects the test 120needs; the test host provides them: 121 122```silo 123:test "vec operations" +Console 124 1 2 3 vec/3 125 .first! 1 = assert 126 "passed" print 127:end 128``` 129 130Tests live alongside the code they test — the convention is 131to put them in the same file inside a `:cfg(test)` block 132(see below), though a test runner can pick them up from 133anywhere. 134 135## Annotations 136 137Annotations are `@`-prefixed metadata on a declaration. Two 138are relevant to everyday code: 139 140- **`@cfg(...)`** — conditional compilation. The annotated 141 declaration is only compiled when the condition holds: 142 143 ```silo 144 @cfg(test) 145 :test "addition works" 146 2 3 + 5 = assert 147 :end 148 149 @cfg(effect: Http) 150 :fn download ( Url -> Bytes ) +Http ... :end 151 152 @cfg(not(test)) 153 :fn production-only ( -> ) ... :end 154 ``` 155 156 Supported conditions: 157 - **`test`** — active during `silo test`. 158 - **`effect: Name`** — only if the host provides that 159 effect. Lets the same source file compile against hosts 160 with or without a given capability. 161 - **`not(...)`** / **`all(...)`** / **`any(...)`** — 162 boolean combinators over the above. 163 164 `@cfg` is evaluated before type-checking, so an inactive 165 branch doesn't have to be well-formed under the current 166 configuration. An `@cfg(effect: Http)`-gated word can call 167 `+Http` words without any fallback needed. 168 169- **`@override`** — suppresses the "shadowing a native impl" 170 warning when you intentionally replace one. The compiler 171 normally warns when a locally-defined impl covers a 172 trait+type that already has a native impl from the trait's 173 or type's home package; `@override` tells it you meant to 174 do that. 175 176 ```silo 177 @override 178 :impl (Display SomeStdType) 179 .fmt ( SomeStdType FmtSpec -> Str ) ... ; 180 :end 181 ``` 182 183Other annotations exist for language-implementation and 184host-glue work (`@lang`, `@host`), but those are not 185routinely written in user code. 186 187## The REPL 188 189Silo's REPL isn't a stripped-down scripting shell — it runs 190the **same frontend and abstract machine** the batch compiler 191does, and the full language surface works inside it. You can: 192 193- Declare types: `:record Point .x F64 .y F64 :end` 194- Declare and impl traits: 195 `:trait Printable self .display ( self -> Str ) :end` 196- Write macros, use them on the next line. 197- Inspect types with `:type-info(Point)` and pattern-match on 198 the result. 199- Define `:init` blocks that populate module-scoped 200 constants, then reference them in subsequent expressions. 201- Run effectful words — the REPL host provides every 202 standard effect. 203 204There is no separate "interactive dialect" with fewer 205features. Compile-time reflection, macros, derive macros, 206dependent types, effect declarations — all of it works the 207same way interactively as it does in a file. 208 209The practical upshot is a feedback loop that feels like a 210notebook or a Lisp REPL: sketch a type, implement a method, 211try a call, evolve the model incrementally, and paste the 212final forms into a `.si` file when you're happy. The 213handbook's 214:gloss[display notation](./A1-glossary.simd#stack) for stack 215output (`⌊...⌉`, top on right) is exactly what the REPL 216draws after each interaction. 217 218## The host 219 220Silo is **:gloss[sans-IO](./A1-glossary.simd#sans-io)**: the 221compiler and the abstract machine never perform I/O 222themselves. Every side effect goes through an 223:gloss[effect handler](./A1-glossary.simd#effect-handler) supplied 224by the host — usually a Rust program that embeds Silo: 225 226``` 227frontend.compile_module(path, source) # parse + type-check 228workspace.link(...) -> ProgramImage # link into runnable form 229machine.start() 230loop { 231 match machine.poll() { 232 RuntimeOutput::EffectRequest { .. } => host handles it, resumes 233 RuntimeOutput::Finished => break 234 ... 235 } 236} 237``` 238 239The host decides which effects it provides. A CLI host 240publishes `+Console`, `+FileSystem`, `+Env`. A browser host 241might publish `+Http`, `+DOM`, `+Storage`. An embedded host 242on a microcontroller might publish `+GPIO` and `+SPI` and 243nothing else. Your Silo code declares the effects it needs 244via `:main`; the compiler rejects programs the host can't 245satisfy. 246 247Because the machine is sans-IO, the same compiled program 248image is portable across hosts that provide the same 249effects. It's also what makes testing, sandboxing, and 250record-and-replay straightforward: none of those are 251features of the language, they fall out of the host being 252able to install whatever effect handler it likes. 253 254## The ecosystem layers 255 256Silo is the middle of a three-layer stack: 257 258- **Silo** — this language. Concatenative core, dependent 259 types, effects, traits. Everything in this book. 260- **The host** — the Rust runtime that embeds Silo's frontend 261 and machine and provides effect handlers. The reference 262 implementation is in `silo-core` / the `silo` CLI. 263- **Tools** — the CLI (`silo` binary) for compiling and 264 running, the REPL for interactive use, `silo doc` for the 265 handbook+API docs (what you're reading right now), and a 266 growing set of language-server-style editor integrations. 267 268## Key points 269 270- `:main` is the binary entry point; its effect row is the 271 program's capability surface. No `:main` for library 272 packages. 273- `:init` runs once per module load in dependency order. 274 Three forms: **named** creates a module-scoped constant 275 word holding the produced value; **anonymous** produces a 276 value available only to `:deinit`; **side-effect-only** 277 (empty output) registers hooks without producing a value. 278 An optional `:deinit` tears down the resource in reverse 279 load order. 280- `:test "name" +effects body :end` declares a test; tests 281 live alongside code, commonly inside `:cfg(test)` blocks. 282- `@cfg(...)` gates a declaration on build-time conditions: 283 `test`, `effect: Name`, or boolean combinations. Inactive 284 declarations aren't type-checked. 285- `@override` silences the warning when a local impl 286 shadows a native one on purpose. Other annotations 287 (`@lang`, `@host`) exist for language-implementation and 288 host-glue work and don't appear in routine code. 289- The REPL runs the same frontend as the batch compiler. 290 Everything that works at compile time — types, traits, 291 impls, macros, `:type-info`, `:init` — works interactively. 292 No reduced dialect. 293- The host embeds Silo and provides effect handlers. Silo 294 itself is sans-IO; portability, sandboxing, and testing 295 fall out of the host being the only party that touches the 296 outside world. 297 298Next: [complete examples](./22-examples.simd) — putting it 299all together with fizzbuzz, binary search, a 300temperature-converter, and a logger-with-aspects.