Source guide/examples.simd

1# Complete Examples
2
3Four worked examples that pull together most of the language.
4Each is a real compilable program; read them front to back to
5see how the pieces fit.
6
7## FizzBuzz
8
9The classic. Iterates a range, matches on divisibility, and
10prints each line.
11
12```silo
13:fn fizzbuzz ( Int -> ) +Console
14  pop-> count
15  0..count [ pop-> i
16    i 1 + pop-> n
17    n 15 mod 0 = :if
18      "FizzBuzz" print
19    :else
20      n 3 mod 0 = :if
21        "Fizz" print
22      :else
23        n 5 mod 0 = :if
24          "Buzz" print
25        :else
26          n "{}" format print
27        :end
28      :end
29    :end
30  ] .for-each
31:end
32
33:main +Console
34  20 fizzbuzz
35:end
36```
37
38Points worth noting:
39
40- `0..count` is a `Range` that the standard library treats as
41  `Foldable`/`Iterator`, so `.for-each` works directly.
42- The quotation `[ pop-> i … ]` captures `count` implicitly —
43  it's a closure.
44- All the arithmetic goes through ordinary trait methods
45  (`mod`, `=`, `+`). `:if` / `:else` chains handle the
46  branching.
47
48## Binary search
49
50Runs on an existential-length `(Vec Int)`. Declares `+Div`
51because the `:loop` break condition is runtime-dependent — the
52compiler can't prove termination statically.
53
54```silo
55:fn binary-search ( (Vec Int) Int -> (Option AnyUInt) ) +Div
56  pop-> target pop-> vec
57  0 pop-> lo
58  vec .len pop-> hi
59  :loop
60    lo hi ≥ :if None :break :end
61    lo hi + 2 / pop-> mid
62    mid vec .get .unwrap pop-> val
63    val target = :if
64      mid Some :break
65    :end
66    val target < :if
67      mid 1 + pop-> lo
68    :else
69      mid pop-> hi
70    :end
71  :end
72:end
73```
74
75The return type `(Option AnyUInt)` says "either an index, or
76none." If the loop breaks with `None`, the search failed; if
77it breaks with `mid Some`, we found the target at position
78`mid`.
79
80## Temperature converter
81
82Two newtype records — `Celsius` and `Fahrenheit` — plus
83`From` impls that convert between them, plus `Display` impls
84for locale-neutral rendering. The newtypes make sure you
85can't accidentally pass a Celsius where a Fahrenheit is
86expected.
87
88```silo
89:record Celsius    .value F64 :end
90:record Fahrenheit .value F64 :end
91
92:impl From Fahrenheit Celsius
93  .from ( Celsius -> Fahrenheit )
94    .value 9.0 * 5.0 / 32.0 + Fahrenheit ;
95:end
96
97:impl From Celsius Fahrenheit
98  .from ( Fahrenheit -> Celsius )
99    .value 32.0 - 5.0 * 9.0 / Celsius ;
100:end
101
102:impl Display Celsius
103  .fmt ( Celsius FmtSpec -> Str )
104    drop .value "{:.1}°C" format ;
105:end
106
107:impl Display Fahrenheit
108  .fmt ( Fahrenheit FmtSpec -> Str )
109    drop .value "{:.1}°F" format ;
110:end
111
112:main +Console
113  100.0 Celsius pop-> boiling
114  boiling "{}" format print             # "100.0°C"
115  boiling (Fahrenheit .from) pop-> f
116  f "{}" format print                   # "212.0°F"
117  f (Celsius .from) "{}" format print   # "100.0°C"
118:end
119```
120
121Notes:
122
123- `(Fahrenheit .from)` is qualified dispatch — it resolves to
124  the specific `From Fahrenheit Celsius` impl.
125- The `FmtSpec` argument to `.fmt` carries width/align/fill;
126  here the impl drops it because it doesn't honour any layout
127  beyond the trait-specific `{:.1}` precision. A richer impl
128  would consult `FmtSpec` to decide whether to pad.
129- `From` auto-generates the reverse `Into`, so `.into` would
130  also work at the call site if a target-typed context were
131  available.
132
133## Simple logger with aspects
134
135The longest example — an aspect-driven logger with a concrete
136handler, invoked through `:with`. Brings together unions,
137aspects, `:impl`, and scoped handler installation.
138
139```silo
140:union LogLevel
141  | LTrace | LDebug | LInfo | LWarn | LError
142:end
143
144:aspect Log
145  .log ( Str LogLevel -> ) drop drop ;
146:end
147
148:record StdoutLogger :end
149
150:impl Log StdoutLogger
151  .log ( Str LogLevel -> ) +Console
152    pop-> level
153    pop-> msg
154    level :match
155      | LTrace => "TRACE"
156      | LDebug => "DEBUG"
157      | LInfo  => "INFO"
158      | LWarn  => "WARN"
159      | LError => "ERROR"
160    :end pop-> label
161    label msg "[{}] {}" format print ;
162:end
163
164:use
165  :aspect logging Log
166:end
167
168:fn process-items ( (Vec Str) -> ) +Console
169  "starting batch" LInfo .log
170  [ pop-> item
171    item "processing: {}" format LDebug .log
172  ] .for-each
173  "batch complete" LInfo .log
174:end
175
176:main +Console
177  StdoutLogger :with Log
178    "a" "b" "c" vec/3 process-items
179  :end
180:end
181```
182
183Output:
184
185```
186[INFO] starting batch
187[DEBUG] processing: a
188[DEBUG] processing: b
189[DEBUG] processing: c
190[INFO] batch complete
191```
192
193A few things worth teasing apart:
194
195- The `.log` signature `( Str LogLevel -> )` has the level on
196  top of the stack — you write `"msg" LInfo .log`, with the
197  level as the "receiver-like" value closest to the call.
198- The default body `drop drop ;` makes the call a no-op when
199  no handler is installed. That's why `.log` has no `+Log`
200  effect in the signatures that call it: the aspect system
201  doesn't need one.
202- `StdoutLogger :with Log … :end` installs the handler for
203  the body's dynamic extent. Inside, every `.log` dispatches
204  to `StdoutLogger`'s impl; outside, the default drops.
205- The `:aspect logging Log` import in `:use` brings the
206  aspect's operation (`.log`) into scope as an ordinary
207  method — no `+Log` annotation anywhere in `process-items`.
208
209## Where to go from here
210
211If you've read the book straight through, you've seen every
212feature the language ships with at least once. A reasonable
213set of next steps:
214
215- Build something small against a CLI host — a line-counter,
216  a config parser, a toy HTTP client. The host-capability
217  story clicks quickly once you have to declare the effects
218  your `:main` needs.
219- Open the `silo:std` source and read through the trait
220  implementations for the primitives. The standard library
221  is deliberately written in Silo (not baked into the host)
222  so that its idioms demonstrate how to use the language in
223  production.
224- Reach for a non-trivial dependent-type pattern — a
225  length-preserving function, a refined-integer port number,
226  a phantom-tagged unit — and work it until the type error
227  becomes legible rather than intimidating.
228- When you want to go deeper on a specific topic, the
229  specification (`spec/*.md`) is the authoritative source.
230  Every rule is numbered (`si[...]`) and cross-referenced;
231  the book points at specific rules where a subtle decision
232  merits it.
233
234The [glossary](./A1-glossary.simd) is an index if you want to
235jump between concepts; the [book root](./mod.simd) has a
236table of contents for browsing.
237
238Thanks for reading.