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.