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.