Repository navigation
Expand file tree
/
Copy pathtutorial.html
More file actions
589 lines (542 loc) · 74.4 KB
/
Copy pathtutorial.html
File metadata and controls
589 lines (542 loc) · 74.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
<!doctype html>
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Grasm in an hour</title>
<meta name="description" content="Grasm in an hour: the guided tutorial, rendered from docs/tutorial.md.">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="preload" href="vendor/fonts/figtree.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="vendor/fonts/sora.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="style.css">
<script src="vendor/motion.js" defer></script>
<script src="vendor/lottie.js" defer></script>
<script type="module" src="app.js"></script>
</head><body data-page="tutorial">
<header class="topbar">
<a class="brand" href="index.html"><svg viewBox="0 0 48 48" width="20" height="20" fill="none" stroke="currentColor" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 6H39"/><path d="M24 14A13 13 0 1 0 37 27H26"/></svg><span>Grasm</span></a>
<span class="ver">v1.0.0</span>
<nav>
<a href="different.html">Why Grasm</a>
<a href="start.html">Docs</a>
<a href="security.html">Security</a>
<a href="achieved.html">Scorecard</a>
<a href="playground.html">Playground</a>
</nav>
<a class="cta" href="start.html">Get started</a>
</header>
<div class="frame">
<nav class="rail">
<a class="mark" href="index.html"><svg viewBox="0 0 48 48" width="18" height="18" fill="none" stroke="currentColor" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 6H39"/><path d="M24 14A13 13 0 1 0 37 27H26"/></svg><b>grasm</b><span>a systems language</span></a>
<h2>Start</h2>
<a href="different.html">Why Grasm</a>
<a href="start.html">Getting started</a>
<a href="install.html">Install</a>
<a href="playground.html">Playground</a>
<h2>Learn</h2>
<a href="tutorial.html" aria-current="page">Tutorial</a>
<a href="tour.html">Tour</a>
<a href="effects.html">Effects</a>
<a href="tiers.html">Ownership</a>
<a href="errors.html">Errors</a>
<a href="concurrency.html">Concurrency</a>
<a href="security.html">Security</a>
<h2>Reference</h2>
<a href="syntax.html">Syntax</a>
<a href="language.html">Language</a>
<a href="library.html">Library</a>
<a href="packages.html">Packages</a>
<a href="spec.html">Specification</a>
<a href="architecture.html">Architecture</a>
<h2>More</h2>
<a href="examples.html">Examples</a>
<a href="achieved.html">Scorecard</a>
<a href="performance.html">Performance</a>
<a href="about.html">About</a>
<a href="status.html">Status</a>
<a href="roadmap.html">Roadmap</a>
</nav>
<main>
<p class="eyebrow">Learn</p>
<h1>Grasm in an hour</h1>
<p class="lede">The whole language in one sitting. This page is
<code>docs/tutorial.md</code>, rendered when the site was written.</p>
<p>A guided tour, in the order the pieces build on each other. Every program here runs — <code>grasm run <file></code> — and the code blocks are the same ones the conformance suite and <code>examples/</code> exercise, so nothing drifts out of date silently.</p>
<p>If you want the exact rules rather than the tour, read <code>spec/</code>. This document explains; the spec decides.</p>
<hr>
<h2 id="1-a-first-program">1. A first program</h2>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"hello, world"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p><code>grasm run hello.gr</code> typechecks and executes. <code>grasm build hello.gr --native</code> produces a standalone binary through Cranelift.</p>
<p>There are no semicolons, no imports needed for <code>print</code>, and no type annotations required where they can be inferred:</p>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">let</span> name <span class="op">=</span> <span class="st">"world"</span>
<span class="kw">let</span> count <span class="op">=</span> <span class="nu">3</span>
<span class="kw">for</span> i <span class="kw">in</span> <span class="nu">1</span><span class="op">..=</span>count <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"hello, {name} ({i})"</span><span class="op">)</span>
<span class="op">}</span>
<span class="op">}</span></code></pre>
<p><code>{...}</code> inside a string interpolates any expression. <code>1..=3</code> includes 3; <code>1..3</code> excludes it.</p>
<h2 id="2-bindings">2. Bindings</h2>
<p><code>let</code> is immutable, <code>var</code> is not. That is the whole rule.</p>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">let</span> x <span class="op">=</span> <span class="nu">1</span>
<span class="kw">var</span> total <span class="op">=</span> <span class="nu">0</span>
total <span class="op">+=</span> x <span class="cm">// fine</span>
<span class="cm">// x = 2 // error: cannot assign to `x` — it is immutable</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{total}"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p>Values have <strong>value semantics</strong>: assigning or passing never creates an alias, so mutating one binding can never be observed through another.</p>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">var</span> a <span class="op">=</span> <span class="op">[</span><span class="nu">1</span><span class="op">,</span> <span class="nu">2</span><span class="op">,</span> <span class="nu">3</span><span class="op">]</span>
<span class="kw">var</span> b <span class="op">=</span> a
b<span class="op">.</span><span class="fn">push</span><span class="op">(</span><span class="nu">4</span><span class="op">)</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{a.len()} {b.len()}"</span><span class="op">)</span> <span class="cm">// 3 4</span>
<span class="op">}</span></code></pre>
<p>Copies are lazy — a copy costs a reference-count bump and only becomes a real copy on mutation of a shared value — but that is an optimization you cannot observe.</p>
<p>A <code>let</code> at module level is a <strong>constant</strong> — one value for the whole program:</p>
<pre><code><span class="kw">let</span> <span class="ty">TABLE_WIDTH</span> <span class="op">=</span> <span class="nu">77</span>
<span class="kw">let</span> <span class="ty">TITLE</span> <span class="op">=</span> <span class="st">"Sales report"</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{TITLE}, {TABLE_WIDTH} wide"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p>A constant's value cannot perform effects. It is evaluated wherever its name is used, so an effect in it would happen once per mention; a value that has to be computed once, with effects, is a function.</p>
<h2 id="3-types">3. Types</h2>
<p>Scalars are <code>i64</code>, <code>f64</code>, <code>bool</code>, <code>str</code>. Collections are <code>[T]</code> and <code>Map[K, V]</code>. Annotate when you want to; infer when you do not.</p>
<pre><code><span class="kw">type</span> <span class="ty">Point</span> <span class="op">=</span> <span class="op">{</span> x<span class="op">:</span> <span class="ty">f64</span><span class="op">,</span> y<span class="op">:</span> <span class="ty">f64</span> <span class="op">}</span>
<span class="kw">type</span> <span class="ty">Shape</span> <span class="op">=</span>
<span class="op">|</span> <span class="ty">Circle</span><span class="op">(</span>r<span class="op">:</span> <span class="ty">f64</span><span class="op">)</span>
<span class="op">|</span> <span class="ty">Rect</span><span class="op">(</span>w<span class="op">:</span> <span class="ty">f64</span><span class="op">,</span> h<span class="op">:</span> <span class="ty">f64</span><span class="op">)</span>
<span class="kw">fn</span> <span class="fn">area</span><span class="op">(</span>s<span class="op">:</span> <span class="ty">Shape</span><span class="op">)</span> <span class="op">-></span> <span class="ty">f64</span> <span class="op">{</span>
<span class="kw">match</span> s <span class="op">{</span>
<span class="ty">Circle</span><span class="op">(</span>r<span class="op">)</span> <span class="op">-></span> <span class="nu">3.14159</span> <span class="op">*</span> r <span class="op">*</span> r
<span class="ty">Rect</span><span class="op">(</span>w<span class="op">,</span> h<span class="op">)</span> <span class="op">-></span> w <span class="op">*</span> h
<span class="op">}</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{area(Circle(2.0))}"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p><code>match</code> must be exhaustive. Adding a variant to <code>Shape</code> turns every incomplete match into a compile error rather than a surprise at runtime.</p>
<p>A tuple can be split into names, in a <code>let</code> or in a loop header:</p>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">let</span> <span class="op">(</span>a<span class="op">,</span> b<span class="op">)</span> <span class="op">=</span> <span class="op">(</span><span class="nu">1</span><span class="op">,</span> <span class="st">"two"</span><span class="op">)</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{a} {b}"</span><span class="op">)</span>
<span class="kw">for</span> <span class="op">(</span>i<span class="op">,</span> w<span class="op">)</span> <span class="kw">in</span> <span class="op">[</span><span class="st">"x"</span><span class="op">,</span> <span class="st">"y"</span><span class="op">]</span><span class="op">.</span><span class="fn">enumerate</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{i}{w}"</span><span class="op">)</span>
<span class="op">}</span>
<span class="op">}</span></code></pre>
<p>Those are the only destructuring forms — there is no <code>if let</code>, and patterns elsewhere appear only in <code>match</code>. They matter most with maps, where <code>entries()</code> hands back pairs, and with <code>enumerate()</code> and <code>zip()</code>.</p>
<p>Write a map with <code>{key: value}</code>, or build one up from <code>{}</code>. Maps iterate in sorted key order, so printing one is deterministic — and what it prints is what you would type to write it back:</p>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">let</span> ripeness <span class="op">=</span> <span class="op">{</span><span class="st">"pear"</span><span class="op">:</span> <span class="nu">2</span><span class="op">,</span> <span class="st">"fig"</span><span class="op">:</span> <span class="nu">1</span><span class="op">}</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{ripeness}"</span><span class="op">)</span> <span class="cm">// {fig: 1, pear: 2} — sorted, every run</span>
<span class="kw">var</span> counts<span class="op">:</span> <span class="ty">Map</span><span class="op">[</span><span class="ty">str</span><span class="op">,</span> <span class="ty">i64</span><span class="op">]</span> <span class="op">=</span> <span class="op">{</span><span class="op">}</span>
counts<span class="op">.</span><span class="fn">set</span><span class="op">(</span><span class="st">"pear"</span><span class="op">,</span> <span class="nu">2</span><span class="op">)</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{counts}"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p><code>{}</code> is the empty map. Braces holding <em>statements</em> are a block, and an empty block has no use as an expression, so <code>()</code> is how you write unit. The braces of <code>if</code>, <code>for</code>, <code>while</code>, <code>match</code> and function bodies are always blocks.</p>
<p>Lists join with <code>+</code>, and sort by a key you extract — which is how a list of structs gets sorted at all, since a struct has no order of its own:</p>
<pre><code><span class="kw">type</span> <span class="ty">Run</span> <span class="op">=</span> <span class="op">{</span> name<span class="op">:</span> <span class="ty">str</span><span class="op">,</span> ms<span class="op">:</span> <span class="ty">i64</span> <span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">var</span> runs <span class="op">=</span> <span class="op">[</span><span class="ty">Run</span><span class="op">{</span> name<span class="op">:</span> <span class="st">"b"</span><span class="op">,</span> ms<span class="op">:</span> <span class="nu">30</span> <span class="op">}</span><span class="op">,</span> <span class="ty">Run</span><span class="op">{</span> name<span class="op">:</span> <span class="st">"a"</span><span class="op">,</span> ms<span class="op">:</span> <span class="nu">10</span> <span class="op">}</span><span class="op">]</span>
runs<span class="op">.</span><span class="fn">sort_by</span><span class="op">(</span><span class="op">|</span>r<span class="op">|</span> r<span class="op">.</span>ms<span class="op">)</span>
<span class="kw">for</span> r <span class="kw">in</span> runs <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{r.name} {r.ms}"</span><span class="op">)</span>
<span class="op">}</span>
<span class="kw">let</span> all <span class="op">=</span> <span class="op">[</span><span class="nu">1</span><span class="op">,</span> <span class="nu">2</span><span class="op">]</span> <span class="op">+</span> <span class="op">[</span><span class="nu">3</span><span class="op">]</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{all}"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p>Text is characters, not bytes, and <code>code_at</code> / <code>codes</code> / <code>to_char</code> move between a character and its Unicode code point — which is what lets you compute on text rather than only cut and compare it:</p>
<pre><code><span class="kw">fn</span> <span class="fn">hash</span><span class="op">(</span>s<span class="op">:</span> <span class="ty">str</span><span class="op">)</span> <span class="op">-></span> <span class="ty">i64</span> <span class="op">{</span>
<span class="kw">var</span> h <span class="op">=</span> <span class="nu">0</span>
<span class="kw">for</span> c <span class="kw">in</span> s<span class="op">.</span><span class="fn">codes</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
h <span class="op">=</span> <span class="op">(</span>h <span class="op">*</span> <span class="nu">31</span> <span class="op">+</span> c<span class="op">)</span> <span class="op">%</span> <span class="nu">1000007</span>
<span class="op">}</span>
h
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{hash("grasm")} {104.to_char()}"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<h2 id="3a-interfaces">3a. Interfaces</h2>
<p>A generic function works on any type — which means that inside it, a <code>T</code> can do almost nothing, because nothing is known about it. An <code>interface</code> names what a type must provide, and a bound says a type parameter provides it:</p>
<pre><code><span class="kw">interface</span> <span class="ty">Show</span> <span class="op">{</span>
<span class="kw">fn</span> <span class="fn">show</span><span class="op">(</span>self<span class="op">)</span> <span class="op">-></span> <span class="ty">str</span>
<span class="op">}</span>
<span class="kw">type</span> <span class="ty">Point</span> <span class="op">=</span> <span class="op">{</span> x<span class="op">:</span> <span class="ty">i64</span><span class="op">,</span> y<span class="op">:</span> <span class="ty">i64</span> <span class="op">}</span>
<span class="kw">impl</span> <span class="ty">Show</span> <span class="kw">for</span> <span class="ty">Point</span> <span class="op">{</span>
<span class="kw">fn</span> <span class="fn">show</span><span class="op">(</span>self<span class="op">)</span> <span class="op">-></span> <span class="ty">str</span> <span class="op">{</span>
<span class="st">"({self.x}, {self.y})"</span>
<span class="op">}</span>
<span class="op">}</span>
<span class="kw">impl</span> <span class="ty">Show</span> <span class="kw">for</span> <span class="ty">i64</span> <span class="op">{</span>
<span class="kw">fn</span> <span class="fn">show</span><span class="op">(</span>self<span class="op">)</span> <span class="op">-></span> <span class="ty">str</span> <span class="op">{</span>
<span class="st">"#{self}"</span>
<span class="op">}</span>
<span class="op">}</span>
<span class="kw">fn</span> all_shown<span class="op">[</span><span class="ty">T</span><span class="op">:</span> <span class="ty">Show</span><span class="op">]</span><span class="op">(</span>xs<span class="op">:</span> <span class="op">[</span><span class="ty">T</span><span class="op">]</span><span class="op">)</span> <span class="op">-></span> <span class="ty">str</span> <span class="op">{</span>
<span class="kw">var</span> out <span class="op">=</span> <span class="st">""</span>
<span class="kw">for</span> x <span class="kw">in</span> xs <span class="op">{</span>
out <span class="op">+=</span> x<span class="op">.</span><span class="fn">show</span><span class="op">(</span><span class="op">)</span> <span class="op">+</span> <span class="st">" "</span>
<span class="op">}</span>
out
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="fn">all_shown</span><span class="op">(</span><span class="op">[</span><span class="ty">Point</span><span class="op">{</span> x<span class="op">:</span> <span class="nu">1</span><span class="op">,</span> y<span class="op">:</span> <span class="nu">2</span> <span class="op">}</span><span class="op">,</span> <span class="ty">Point</span><span class="op">{</span> x<span class="op">:</span> <span class="nu">3</span><span class="op">,</span> y<span class="op">:</span> <span class="nu">4</span> <span class="op">}</span><span class="op">]</span><span class="op">)</span><span class="op">)</span>
<span class="fn">print</span><span class="op">(</span><span class="fn">all_shown</span><span class="op">(</span><span class="op">[</span><span class="nu">7</span><span class="op">,</span> <span class="nu">8</span><span class="op">]</span><span class="op">)</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p><code>self</code> is the value the method is called on, and <code>Self</code> in an interface means "the implementing type". The method is chosen by the receiver's type, so <code>Point</code> and <code>i64</code> can each have a <code>show</code>.</p>
<p>The bound is checked where the function is written, not where it is used: <code>x.show()</code> in a function declared <code>[T]</code> rather than <code>[T: Show]</code> is an error even if every caller happens to pass something showable. And a call with a type that has no <code>impl</code> is refused at that call, by name — "<code>Dog</code> does not implement <code>Show</code>" — rather than somewhere inside the generic function.</p>
<p>An interface method may declare an effect row, and then its implementations may perform it:</p>
<pre><code><span class="kw">interface</span> <span class="ty">Sink</span> <span class="op">{</span>
<span class="kw">fn</span> <span class="fn">emit</span><span class="op">(</span>self<span class="op">,</span> line<span class="op">:</span> <span class="ty">str</span><span class="op">)</span> <span class="op">/</span> <span class="op">{</span>io<span class="op">}</span>
<span class="op">}</span>
<span class="kw">fn</span> send_all<span class="op">[</span><span class="ty">T</span><span class="op">:</span> <span class="ty">Sink</span><span class="op">]</span><span class="op">(</span>s<span class="op">:</span> <span class="ty">T</span><span class="op">,</span> xs<span class="op">:</span> <span class="op">[</span><span class="ty">str</span><span class="op">]</span><span class="op">)</span> <span class="op">/</span> <span class="op">{</span>io<span class="op">}</span> <span class="op">{</span>
<span class="kw">for</span> x <span class="kw">in</span> xs <span class="op">{</span>
s<span class="op">.</span><span class="fn">emit</span><span class="op">(</span>x<span class="op">)</span>
<span class="op">}</span>
<span class="op">}</span></code></pre>
<p><code>send_all</code> has to declare <code>io</code> itself, because a call through a bound performs whatever the <em>interface</em> said — which is how a generic function's row stays honest without knowing which <code>impl</code> it will reach. An <code>impl</code> may not declare more than its interface does, so nothing can smuggle an effect past a package's capabilities. An interface method with no row performs nothing.</p>
<p>An <code>impl</code> is for a <em>shape</em>, and may introduce its own bounded parameters:</p>
<pre><code><span class="kw">interface</span> <span class="ty">Show</span> <span class="op">{</span>
<span class="kw">fn</span> <span class="fn">show</span><span class="op">(</span>self<span class="op">)</span> <span class="op">-></span> <span class="ty">str</span>
<span class="op">}</span>
<span class="kw">impl</span> <span class="ty">Show</span> <span class="kw">for</span> <span class="ty">i64</span> <span class="op">{</span>
<span class="kw">fn</span> <span class="fn">show</span><span class="op">(</span>self<span class="op">)</span> <span class="op">-></span> <span class="ty">str</span> <span class="op">{</span>
<span class="st">"{self}"</span>
<span class="op">}</span>
<span class="op">}</span>
<span class="kw">impl</span><span class="op">[</span><span class="ty">T</span><span class="op">:</span> <span class="ty">Show</span><span class="op">]</span> <span class="ty">Show</span> <span class="kw">for</span> <span class="op">[</span><span class="ty">T</span><span class="op">]</span> <span class="op">{</span>
<span class="kw">fn</span> <span class="fn">show</span><span class="op">(</span>self<span class="op">)</span> <span class="op">-></span> <span class="ty">str</span> <span class="op">{</span>
<span class="kw">var</span> out <span class="op">=</span> <span class="st">"["</span>
<span class="kw">var</span> first <span class="op">=</span> <span class="kw">true</span>
<span class="kw">for</span> v <span class="kw">in</span> self <span class="op">{</span>
<span class="kw">if</span> <span class="kw">not</span> first <span class="op">{</span> out <span class="op">=</span> out <span class="op">+</span> <span class="st">", "</span> <span class="op">}</span>
out <span class="op">=</span> out <span class="op">+</span> v<span class="op">.</span><span class="fn">show</span><span class="op">(</span><span class="op">)</span>
first <span class="op">=</span> <span class="kw">false</span>
<span class="op">}</span>
out <span class="op">+</span> <span class="st">"]"</span>
<span class="op">}</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">/</span> <span class="op">{</span>io<span class="op">}</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="op">[</span><span class="op">[</span><span class="nu">1</span><span class="op">,</span> <span class="nu">2</span><span class="op">]</span><span class="op">,</span> <span class="op">[</span><span class="nu">3</span><span class="op">]</span><span class="op">]</span><span class="op">.</span><span class="fn">show</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p>One <code>impl</code> then serves <code>[i64]</code>, <code>[Point]</code> and <code>[[i64]]</code> alike -- the last satisfies the bound through this very <code>impl</code>. A declared generic type is a shape too (<code>impl[A: Show] Show for Pair[A]</code>), and takes the parameters it was declared with. Each shape has one <code>impl</code> of each interface, so no two can overlap and there is never a question of which one runs.</p>
<h2 id="4-errors-are-values">4. Errors are values</h2>
<p>There are no exceptions. A function that can fail says so in its type with <code>! E</code>, raises with <code>fail</code>, and callers propagate with <code>try</code>.</p>
<pre><code><span class="kw">fn</span> <span class="fn">divide</span><span class="op">(</span>a<span class="op">:</span> <span class="ty">i64</span><span class="op">,</span> b<span class="op">:</span> <span class="ty">i64</span><span class="op">)</span> <span class="op">-></span> <span class="ty">i64</span> <span class="op">!</span> <span class="ty">str</span> <span class="op">{</span>
<span class="kw">if</span> b <span class="op">==</span> <span class="nu">0</span> <span class="op">{</span>
<span class="fn">fail</span><span class="op">(</span><span class="st">"division by zero"</span><span class="op">)</span>
<span class="op">}</span>
a <span class="op">/</span> b
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">compute</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="ty">i64</span> <span class="op">!</span> <span class="ty">str</span> <span class="op">{</span>
<span class="kw">let</span> half <span class="op">=</span> <span class="kw">try</span> <span class="fn">divide</span><span class="op">(</span><span class="nu">10</span><span class="op">,</span> <span class="nu">2</span><span class="op">)</span>
<span class="kw">try</span> <span class="fn">divide</span><span class="op">(</span>half<span class="op">,</span> <span class="nu">0</span><span class="op">)</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">match</span> <span class="fn">compute</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="ty">Ok</span><span class="op">(</span>v<span class="op">)</span> <span class="op">-></span> <span class="fn">print</span><span class="op">(</span><span class="st">"got {v}"</span><span class="op">)</span>
<span class="ty">Err</span><span class="op">(</span>e<span class="op">)</span> <span class="op">-></span> <span class="fn">print</span><span class="op">(</span><span class="st">"failed: {e}"</span><span class="op">)</span>
<span class="op">}</span>
<span class="op">}</span></code></pre>
<p>You cannot forget to handle one: using a fallible value without <code>try</code> or a <code>match</code> is a compile error. An error that reaches <code>main</code> exits 1 with a message rather than exiting 0 as though nothing happened.</p>
<p>A <strong>panic</strong> is different: it means a bug (index out of bounds, divide by zero, a failed <code>assert</code>), prints <code>panic: <message></code>, and exits 101. Panics are not for recoverable conditions.</p>
<p><code>Option</code> covers absence:</p>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">let</span> xs <span class="op">=</span> <span class="op">[</span><span class="nu">3</span><span class="op">,</span> <span class="nu">1</span><span class="op">,</span> <span class="nu">4</span><span class="op">]</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{xs.min() ?? 0}"</span><span class="op">)</span> <span class="cm">// 1</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{xs.find(|x| x > 99) ?? -1}"</span><span class="op">)</span> <span class="cm">// -1</span>
<span class="op">}</span></code></pre>
<p><code>??</code> evaluates its right side only when the left is <code>None</code>.</p>
<h2 id="4a-contracts">4a. Contracts</h2>
<p>A function can state what it expects and what it promises, and every call checks both:</p>
<pre><code><span class="kw">fn</span> <span class="fn">isqrt</span><span class="op">(</span>n<span class="op">:</span> <span class="ty">i64</span><span class="op">)</span> <span class="op">-></span> <span class="ty">i64</span>
requires n <span class="op">>=</span> <span class="nu">0</span>
ensures <span class="op">|</span>r<span class="op">|</span> r <span class="op">*</span> r <span class="op"><=</span> n <span class="kw">and</span> <span class="op">(</span>r <span class="op">+</span> <span class="nu">1</span><span class="op">)</span> <span class="op">*</span> <span class="op">(</span>r <span class="op">+</span> <span class="nu">1</span><span class="op">)</span> <span class="op">></span> n
<span class="op">{</span>
<span class="kw">var</span> r <span class="op">=</span> <span class="nu">0</span>
<span class="kw">while</span> <span class="op">(</span>r <span class="op">+</span> <span class="nu">1</span><span class="op">)</span> <span class="op">*</span> <span class="op">(</span>r <span class="op">+</span> <span class="nu">1</span><span class="op">)</span> <span class="op"><=</span> n <span class="op">{</span>
r <span class="op">+=</span> <span class="nu">1</span>
<span class="op">}</span>
r
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{isqrt(17)} {isqrt(1000000)}"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p><code>requires</code> is checked before the body runs; <code>ensures |r|</code> is checked on the result, however the body leaves. A broken one panics and quotes itself — <code>requires</code>n >= 0<code>failed in</code>isqrt<code>` — so a wrong answer stops at the function that produced it instead of three calls later. A contract is a plain</code>bool` that performs nothing: it states a fact, it does not do work.</p>
<p><code>grasm test --contracts</code> turns the contracts into tests. For each function with an <code>ensures</code> it generates inputs, sets aside the ones <code>requires</code> turns away, and reports any input whose result breaks the promise, shrunk to a small one:</p>
<pre><code>test "contracts of isqrt" ... ok (100 cases, 48 outside `requires`)
test "contracts of sloppy" ... FAILED on case 79 of 300
ensures `|r| r * r <= n and (r + 1) * (r + 1) > n` failed in `sloppy`
counterexample: n = 289</code></pre>
<p><code>grasm test --schedules N</code> does the same for tasks. A test that starts tasks in a <code>concurrent</code> block runs again under <code>N</code> other orders in which they could have taken turns; a result that depends on the order fails, with the seed that reproduces it:</p>
<pre><code>test "tasks arrive in go order" ... FAILED: assertion failed: `[21]` != `[12]`
under schedule 2 of 20
replay: --schedule-seed 13899076551742739803</code></pre>
<h2 id="5-effects">5. Effects</h2>
<p>A function's type says what it <em>does</em>, not just what it returns. Built-in effects are <code>io</code> and <code>async</code>; you can declare your own.</p>
<pre><code><span class="kw">effect</span> <span class="ty">Clock</span> <span class="op">{</span>
<span class="kw">fn</span> <span class="fn">now</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="ty">i64</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">stamp</span><span class="op">(</span><span class="op">)</span> <span class="op">-></span> <span class="ty">str</span> <span class="op">/</span> <span class="op">{</span><span class="ty">Clock</span><span class="op">}</span> <span class="op">{</span>
<span class="st">"at {Clock.now()}"</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">with</span> <span class="ty">Clock</span> <span class="kw">handled_by</span> <span class="ty">ClockHandler</span><span class="op">{</span> now<span class="op">:</span> <span class="op">|</span><span class="op">|</span> <span class="nu">1700000000</span> <span class="op">}</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="fn">stamp</span><span class="op">(</span><span class="op">)</span><span class="op">)</span>
<span class="op">}</span>
<span class="op">}</span></code></pre>
<p>An effect that reaches <code>main</code> without a handler is a <em>compile</em> error. This is what makes tests hermetic by construction: a function that touches the clock or the network cannot pretend otherwise, and a test supplies a handler instead of a mocking framework.</p>
<p>The same property is what the package manager enforces at the dependency boundary — see §8.</p>
<h2 id="6-ownership-without-a-borrow-checker">6. Ownership without a borrow checker</h2>
<p>Three parameter modes, and the default is the one you want:</p>
<ul><li>plain — the callee gets a value; the caller keeps theirs.</li><li><code>inout</code> — the callee mutates the caller's binding in place. The caller passes a mutable place, and the same place cannot be passed as <code>inout</code> twice in one call.</li><li><code>own</code> — the caller gives the value up. Using it afterwards is a compile error, <strong>for types that are not copied</strong>. <code>i64</code>, <code>f64</code>, <code>bool</code>, and <code>()</code> are copied ([T-COPY]), so <code>own</code> on one of those transfers nothing and the caller keeps using its binding. <code>str</code>, lists, maps, and structs do move. Write the demonstration with one of those, or it will appear to disprove the rule it is demonstrating:</li></ul>
<pre><code><span class="kw">fn</span> <span class="fn">consume</span><span class="op">(</span><span class="kw">own</span> xs<span class="op">:</span> <span class="op">[</span><span class="ty">i64</span><span class="op">]</span><span class="op">)</span> <span class="op">-></span> <span class="ty">i64</span> <span class="op">{</span>
xs<span class="op">.</span><span class="fn">len</span><span class="op">(</span><span class="op">)</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">let</span> xs <span class="op">=</span> <span class="op">[</span><span class="nu">1</span><span class="op">,</span> <span class="nu">2</span><span class="op">,</span> <span class="nu">3</span><span class="op">,</span> <span class="nu">4</span><span class="op">,</span> <span class="nu">5</span><span class="op">]</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{consume(xs)}"</span><span class="op">)</span>
<span class="cm">// print("{xs.len()}") // error: use of moved value `xs`</span>
<span class="op">}</span></code></pre>
<pre><code><span class="kw">fn</span> <span class="fn">bump</span><span class="op">(</span><span class="kw">inout</span> n<span class="op">:</span> <span class="ty">i64</span><span class="op">)</span> <span class="op">{</span>
n <span class="op">+=</span> <span class="nu">1</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">var</span> count <span class="op">=</span> <span class="nu">0</span>
<span class="fn">bump</span><span class="op">(</span>count<span class="op">)</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{count}"</span><span class="op">)</span> <span class="cm">// 1</span>
<span class="op">}</span></code></pre>
<p>The mode lives on the parameter, so the call site stays plain. No lifetimes, no borrow annotations on types, no <code>&</code>/<code>&mut</code> sprinkled through the code.</p>
<h2 id="6a-functions-as-parameters">6a. Functions as parameters</h2>
<p><code>fn(A, B) -> R</code> is a type, so a program can declare its own higher-order function rather than being limited to the built-in ones:</p>
<pre><code><span class="kw">fn</span> <span class="fn">apply</span><span class="op">(</span>f<span class="op">:</span> <span class="kw">fn</span><span class="op">(</span><span class="ty">i64</span><span class="op">)</span> <span class="op">-></span> <span class="ty">i64</span><span class="op">,</span> x<span class="op">:</span> <span class="ty">i64</span><span class="op">)</span> <span class="op">-></span> <span class="ty">i64</span> <span class="op">{</span>
<span class="fn">f</span><span class="op">(</span>x<span class="op">)</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">double</span><span class="op">(</span>n<span class="op">:</span> <span class="ty">i64</span><span class="op">)</span> <span class="op">-></span> <span class="ty">i64</span> <span class="op">{</span>
n <span class="op">*</span> <span class="nu">2</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{apply(double, 5)}"</span><span class="op">)</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{apply(|n| n + 1, 5)}"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p>A closure passed to such a parameter takes its parameter types from it, so it needs no annotation.</p>
<p>A closure captures the <strong>values</strong> it uses at the moment it is created, not the bindings ([E-CLOSURE-CAPTURE]). Changing a captured variable afterwards is not visible inside, and assigning to one inside changes only the closure's copy:</p>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">var</span> n <span class="op">=</span> <span class="nu">1</span>
<span class="kw">let</span> plus_n <span class="op">=</span> <span class="op">|</span>x<span class="op">|</span> x <span class="op">+</span> n
n <span class="op">=</span> <span class="nu">100</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{plus_n(0)}"</span><span class="op">)</span> <span class="cm">// 1, not 100</span>
<span class="op">}</span></code></pre>
<p>That has one consequence worth knowing before you design around it: <strong>an effect handler cannot hold mutable state</strong>, because a handler's operations are closures. A <code>Store</code> whose <code>write</code> updates a captured list updates a copy, and the next <code>read</code> will not see it. Handlers supply values and accept output; state that changes belongs in the caller. <code>examples/ledger/</code> is that lesson written down after getting it wrong.</p>
<p>Note that <code>spawn { … }</code> does <em>not</em> work this way — a task body shares the enclosing scope ([E-SPAWN-SHARED]). Two block-shaped constructs, two rules.</p>
<p>The type carries an effect row, and <strong>no row means the empty row</strong>. So <code>fn(i64) -> i64</code> accepts only functions that perform nothing, and passing one that prints is an error where you pass it — not a surprise inside. Write <code>fn(i64) -> i64 / {io}</code> when the caller should be allowed to hand over something effectful. This is the one place a row is written rather than inferred, because a type has no body to infer from.</p>
<h2 id="6b-values-that-must-be-used-exactly-once">6b. Values that must be used exactly once</h2>
<p>Some values stand for something that must be finished: a transaction to commit or roll back, a file to close, a lock to release. A <code>linear type</code> makes forgetting a compile error instead of a production incident:</p>
<pre><code>linear <span class="kw">type</span> <span class="ty">Tx</span> <span class="op">=</span> <span class="op">{</span> id<span class="op">:</span> <span class="ty">i64</span> <span class="op">}</span>
<span class="kw">fn</span> <span class="fn">open</span><span class="op">(</span>id<span class="op">:</span> <span class="ty">i64</span><span class="op">)</span> <span class="op">-></span> <span class="ty">Tx</span> <span class="op">{</span>
<span class="ty">Tx</span><span class="op">{</span> id<span class="op">:</span> id <span class="op">}</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">commit</span><span class="op">(</span><span class="kw">own</span> t<span class="op">:</span> <span class="ty">Tx</span><span class="op">)</span> <span class="op">-></span> <span class="ty">str</span> <span class="op">{</span>
<span class="st">"committed {t.id}"</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">rollback</span><span class="op">(</span><span class="kw">own</span> t<span class="op">:</span> <span class="ty">Tx</span><span class="op">)</span> <span class="op">-></span> <span class="ty">str</span> <span class="op">{</span>
<span class="st">"rolled back {t.id}"</span>
<span class="op">}</span>
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">let</span> t <span class="op">=</span> <span class="fn">open</span><span class="op">(</span><span class="nu">1</span><span class="op">)</span>
<span class="kw">if</span> t<span class="op">.</span>id <span class="op">></span> <span class="nu">0</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="fn">commit</span><span class="op">(</span>t<span class="op">)</span><span class="op">)</span>
<span class="op">}</span> <span class="kw">else</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="fn">rollback</span><span class="op">(</span>t<span class="op">)</span><span class="op">)</span>
<span class="op">}</span>
<span class="op">}</span></code></pre>
<p>Each <code>Tx</code> must be handed on exactly once on every path: to an <code>own</code> parameter, returned, or moved into another binding. Reading <code>t.id</code> or lending <code>t</code> to an ordinary parameter does not count. Delete the <code>else</code> branch and the program no longer compiles — <code>t</code> would be consumed on one path and not the other — and calling <code>commit(t)</code> twice is a use of a moved value. <code>commit</code> and <code>rollback</code> may simply let their <code>own</code> parameter end, because they are in the module that declares <code>Tx</code>; that is how a linear value is finished.</p>
<h2 id="7-structured-concurrency">7. Structured concurrency</h2>
<p><code>parallel</code> opens a scope; <code>spawn</code> adds a task to it. The scope does not exit until every task inside it has finished.</p>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">var</span> log<span class="op">:</span> <span class="op">[</span><span class="ty">str</span><span class="op">]</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>
parallel <span class="op">{</span>
spawn <span class="op">{</span>
log<span class="op">.</span><span class="fn">push</span><span class="op">(</span><span class="st">"first"</span><span class="op">)</span>
<span class="op">}</span>
spawn <span class="op">{</span>
log<span class="op">.</span><span class="fn">push</span><span class="op">(</span><span class="st">"second"</span><span class="op">)</span>
<span class="op">}</span>
<span class="op">}</span>
<span class="fn">print</span><span class="op">(</span>log<span class="op">.</span><span class="fn">join</span><span class="op">(</span><span class="st">" "</span><span class="op">)</span><span class="op">)</span> <span class="cm">// first second</span>
<span class="op">}</span></code></pre>
<p>Tasks run in spawn order, deterministically: the same program always produces the same output. That determinism is deliberate and is <em>not</em> multi-core speedup — <code>parallel</code> buys you structure and reproducibility, not throughput, because its tasks share the enclosing scope and running them at once would race.</p>
<p>Throughput has its own form, and the effect system is what makes it safe:</p>
<pre><code><span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="kw">let</span> xs <span class="op">=</span> <span class="op">[</span><span class="nu">1</span><span class="op">,</span> <span class="nu">2</span><span class="op">,</span> <span class="nu">3</span><span class="op">,</span> <span class="nu">4</span><span class="op">,</span> <span class="nu">5</span><span class="op">]</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{xs.par_map(|x| x * x)}"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p><code>par_map</code> and <code>par_filter</code> spread the work across cores. The function must be effect-free and <strong>the compiler proves it</strong> — put a <code>print</code> inside and the build fails — so there are no <code>Send</code>/<code>Sync</code> bounds to satisfy and no race detector to run. Results come back in input order whatever the core count is, so the answer never depends on the machine.</p>
<p>Which is also why only the compiled backend actually uses them: <code>grasm run</code> and <code>grasm build -o</code> produce a bundled <em>interpreter</em>, and there a <code>par_map</code> is a <code>map</code>. The answer is identical — that is the whole point of requiring purity — but the throughput is not, so measure with <code>grasm build --native</code>.</p>
<p>A short list is not automatically a sequential one. The runtime computes the first element on the calling thread, times it, and splits the rest when what remains is worth more than the spawns — so six elements of real work get the cores, and a thousand cheap ones are not split for the sake of it. Being wrong costs one element's time and never changes the answer.</p>
<p>What threads cannot help with is worth knowing before reaching for them: if the body mostly <em>moves</em> memory rather than computing — copying a buffer, building a list — eight workers share one memory bus and gain nothing, and if the body reads a large structure shared with the other workers, the reference counts on it are atomic and the contention can cost more than the work. <code>docs/apps-phases.md</code> has both measured, from an audio mixdown where the difference was 1.2 s against 6.3 s.</p>
<p>A panicking task aborts the whole scope; tasks spawned after it do not run. A <code>return</code> inside a task body returns from the function the <code>parallel</code> block is in, and the rest of the scope — the tasks not yet run, and the statements after the block — is abandoned. That is a way out, not a way to end one task.</p>
<p>The two forms divide the work between them, and it is worth knowing which you want before you reach for one. <strong><code>for x in xs { spawn { … x … } }</code> is not writable</strong>: a task's body reads the enclosing scope as it stands when the tasks run, so a loop variable is gone by then, and the compiler says so. The number of tasks a <code>parallel</code> block starts is fixed where it is written.</p>
<p>So: a <em>runtime-sized</em> amount of work over data is <code>par_map</code>/<code>par_filter</code>, which is also the form that gets real cores and that the compiler proves safe. <code>parallel</code>/<code>spawn</code> is for a fixed set of distinct activities that share results through the enclosing scope.</p>
<h2 id="8-modules-and-packages">8. Modules and packages</h2>
<pre><code><span class="kw">import</span> lib<span class="op">.</span>math
<span class="kw">fn</span> <span class="fn">main</span><span class="op">(</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">print</span><span class="op">(</span><span class="st">"{math.square(4)}"</span><span class="op">)</span>
<span class="op">}</span></code></pre>
<p><code>pub</code> controls what crosses a module boundary. <code>import helper</code> finds <code>helper.gr</code> beside the importing file; <code>import lib.math</code> finds <code>lib/math.gr</code>. No manifest is needed for either — a manifest is what turns a directory into a <em>package</em>, which is what dependencies and capabilities are about.</p>
<p>A package is a directory laid out like this:</p>
<pre><code>mathlib<span class="op">/</span>
grasm<span class="op">.</span>toml the manifest
src<span class="op">/</span>
main<span class="op">.</span>gr the entry file — what `<span class="kw">import</span> mathlib` resolves to</code></pre>
<p><code>src/main.gr</code> is the default; <code>entry = "src/lib.gr"</code> in <code>[package]</code> changes it. It has to name a file <em>inside</em> the package: the lockfile's checksum, the capability ceiling and <code>grasm publish</code> all cover the package's own directory, so an <code>entry</code> reaching out of it with <code>..</code> — or an absolute path — is refused rather than silently leaving all three describing a tree the program does not use. It has to end in <code>.gr</code> for the same reason: those three collect the package's Grasm sources, and an entry named anything else was executed but recorded nowhere — <code>--locked</code> reported "up to date" after it changed, and <code>grasm publish</code> shipped a package that could not build into a slot that cannot be republished. Everything a package exports lives behind that one file, so a dependency's other modules are reached as <code>import mathlib.helpers</code>.</p>
<p>"The package's sources" is <strong>one</strong> answer to all three questions, which it was not: the checksum skipped <code>target/</code> and every dot-directory, both of which are legal import path segments; the <code>.gr</code> extension was matched case-sensitively, so <code>HELPER.GR</code> — what <code>import helper</code> resolves to on a case-insensitive filesystem — was invisible to the checksum, to <code>publish</code> and to the ceiling alike; and <code>publish</code> walked from the entry's directory while the checksum walked from the package's, so one released version had two checksums. A directory symlink is followed once: a package holding two self-links took <code>deps</code> and <code>publish</code> from "34 copies into an immutable slot" to "never returns".</p>
<p>A package with a <code>path</code> dependency cannot be published at all. Only the package's own directory travels, so the release named a dependency that resolved outside the version directory — every consumer failed on a version that cannot be republished.</p>
<pre><code>[package]
name = "mathlib"
version = "0.1.0"
capabilities = [] # this package performs no effects
[dependencies.utils]
path = "../utils"
version = "^1.0.0" # optional: 1.x, and no other line</code></pre>
<p><code>version</code> accepts a compatible range with <code>^</code> (a bare <code>1.0.0</code> means the same) or one exact version with <code>=</code>. Before 1.0 the minor number carries the weight of a major, so <code>^0.2.0</code> stops below <code>0.3.0</code>.</p>
<p>Drop the <code>path</code> and the package comes from a registry — a directory of <code><name>/<version>/</code> packages, pointed at by <code>GRASM_REGISTRY</code> or found as the nearest <code>.grasm/registry</code>. Which version you get is the <em>lowest</em> one that satisfies everybody, so adding a dependency never quietly upgrades something else you were already using.</p>
<p><code>capabilities</code> is a <strong>ceiling</strong>, and it is checked against what the code actually does. <strong>Omitting the key means unrestricted</strong>, not empty — a package with no <code>capabilities</code> line is not held to anything, and <code>grasm deps</code> reports it as <code>(unrestricted)</code>. Write <code>capabilities = []</code> to mean "performs nothing"; leaving it out means "do not check me". A key <code>[package]</code> does not recognise is refused rather than ignored, because that difference is exactly what a typo would spend: <code>capabilitys = []</code> used to leave the package unrestricted, and the only trace was a column in <code>grasm deps</code>. Because effects are inferred from source, a dependency that quietly starts reading the filesystem exceeds its declared ceiling and fails to build <strong>in every consumer</strong> — a compile error, not a runtime surprise, and not something a reviewer has to happen to notice.</p>
<p>A capability is what a package needs from <strong>outside</strong> itself, so what counts is what <em>escapes</em>: the effects <code>main</code> performs, the effects left in a <code>pub</code> function's row for a consumer to handle, and the effects its <code>test</code> and <code>bench</code> bodies and its <strong>doc examples</strong> perform. That last group surprises people — a test is not part of the interface, and an example is documentation — but <code>grasm test</code> runs the tests <em>and the doc examples</em> of every imported module, so a dependency's test body and its <code>///</code> fenced blocks really do execute in your process. The consequence is worth knowing: a <code>capabilities = []</code> package cannot <code>print</code> from a test or from a doc example, which rules out the usual <code>/// print("{f(2)}")</code> — write the example as <code>assert(f(2) == 4)</code> instead, which performs nothing and checks more. Every module in the package counts, not only the ones its entry file imports — a consumer reaches <code>pub fn f</code> with <code>import dep.module</code> whether or not the dependency's own entry mentions that module, so a <code>capabilities = []</code> package could otherwise keep a filesystem reader in an unimported sibling file and be reported as performing nothing. A <em>dependency's</em> <code>pub</code> rows do not count toward the consumer, whose ceiling is what it needs, not what everything it depends on offers; what the consumer actually calls is already in the row of whatever calls it. An effect you declare, perform and handle inside your own package needs nothing from anyone and is not a capability — but the handler answers for what it really does, so a <code>Log</code> handler that calls <code>print</code> puts <code>io</code> in the ceiling whoever asked for it.</p>
<p><code>grasm deps</code> resolves the graph, checks every package against its ceiling, and writes <code>grasm.lock</code> with a SHA-256 over each package's manifest and sources. The manifest is in the digest because the <code>capabilities</code> line is what a consumer is relying on: three releases with identical code and different ceilings hashed the same without it.</p>
<h2 id="9-testing">9. Testing</h2>
<p>One declaration form covers three kinds of test.</p>
<pre><code><span class="kw">fn</span> <span class="fn">reverse</span><span class="op">(</span>xs<span class="op">:</span> <span class="op">[</span><span class="ty">i64</span><span class="op">]</span><span class="op">)</span> <span class="op">-></span> <span class="op">[</span><span class="ty">i64</span><span class="op">]</span> <span class="op">{</span>
<span class="kw">var</span> out<span class="op">:</span> <span class="op">[</span><span class="ty">i64</span><span class="op">]</span> <span class="op">=</span> <span class="op">[</span><span class="op">]</span>
<span class="kw">var</span> i <span class="op">=</span> xs<span class="op">.</span><span class="fn">len</span><span class="op">(</span><span class="op">)</span> <span class="op">-</span> <span class="nu">1</span>
<span class="kw">while</span> i <span class="op">>=</span> <span class="nu">0</span> <span class="op">{</span>
out<span class="op">.</span><span class="fn">push</span><span class="op">(</span>xs<span class="op">[</span>i<span class="op">]</span><span class="op">)</span>
i <span class="op">=</span> i <span class="op">-</span> <span class="nu">1</span>
<span class="op">}</span>
out
<span class="op">}</span>
<span class="kw">test</span> <span class="st">"reverse of a known list"</span> <span class="op">{</span>
<span class="fn">assert</span><span class="op">(</span><span class="fn">reverse</span><span class="op">(</span><span class="op">[</span><span class="nu">1</span><span class="op">,</span> <span class="nu">2</span><span class="op">,</span> <span class="nu">3</span><span class="op">]</span><span class="op">)</span> <span class="op">==</span> <span class="op">[</span><span class="nu">3</span><span class="op">,</span> <span class="nu">2</span><span class="op">,</span> <span class="nu">1</span><span class="op">]</span><span class="op">)</span>
<span class="op">}</span>
<span class="kw">test</span> <span class="st">"reversing twice is the identity"</span> <span class="op">(</span>xs<span class="op">:</span> <span class="op">[</span><span class="ty">i64</span><span class="op">]</span><span class="op">)</span> <span class="op">{</span>
<span class="fn">assert</span><span class="op">(</span><span class="fn">reverse</span><span class="op">(</span><span class="fn">reverse</span><span class="op">(</span>xs<span class="op">)</span><span class="op">)</span> <span class="op">==</span> xs<span class="op">)</span>
<span class="op">}</span></code></pre>
<p><code>grasm test file.gr</code> runs the tests of every module the program imports, not just that file's, and names each one by its module. <code>grasm bench</code> does the same for benchmarks. A library whose tests are failing should not look like a library whose tests passed.</p>
<p>Parameters make it a <strong>property</strong> test: the runner generates the inputs. Pointing one at <code>str</code> is fuzzing — the generator emits tabs, newlines, quotes, backslashes, and multi-byte characters.</p>
<p>A failure reports a <em>shrunk</em> counterexample — the smallest input that still fails — plus the seed that found it:</p>
<pre><code>test "doubling is monotonic" ... FAILED on case 1 of 100
assertion failed
counterexample: n = 0
replay: --seed 24301 --cases 100</code></pre>
<p><code>0</code> is the simplest witness the shrinker could reach: <code>0 * 2</code> is <code>0</code>, which is not greater. The property is false for <code>0</code>, for every negative number, and for <code>4611686018427387904</code> and up, where <code>n * 2</code> wraps — the shrinker reports the simplest one it can, not the first one it found.</p>
<p>That seed is the <em>default</em>, and it does not change between runs: <code>grasm test</code> twice runs the same hundred cases twice, so a suite that passes today passes tomorrow. Running it again is not how you widen coverage — <code>--seed</code> is. What the fixed run does guarantee is the boundary values: the first ten cases walk them in order, so <code>0</code>, <code>±1</code>, <code>±2</code>, <code>10</code>, <code>100</code>, the <code>i64</code> extremes, NaN, both infinities, both zeros, both <code>bool</code>s and the empty string are tried every time rather than merely often. The rest of the run is the widening random mix.</p>
<p><code>grasm test --coverage</code> reports which statement lines the suite reached — counting the lines that doc examples reach, since those are tests too. It did not, so a function reached <em>only</em> by its own documented example was reported uncovered while the suite had just run it — and then it did, but only for the entry file: a group of examples runs in a synthesized program where the owner's source is inlined, so the lines an <em>imported</em> module's examples reached were being read against the wrong file. <code>bench "name" { ... }</code> bodies are warmed up, then sampled, and <code>grasm bench</code> reports time per operation, in whatever unit keeps the figure readable.</p>
<h2 id="10-documentation-that-cannot-rot">10. Documentation that cannot rot</h2>
<p><code>///</code> comments document the declaration that follows. <code>grasm doc</code> renders them as Markdown with signatures taken from the AST, so a documented effect row or error type cannot drift from the real one.</p>
<pre><code>/// Adds two integers.
///
/// Addition wraps on overflow.
///
/// ```
/// assert(add(2, 3) == 5)
/// ```
fn add(a: i64, b: i64) -> i64 {
a + b
}
````
The fenced block is **executable**. `grasm test` runs it as a test, compiled
in the module that owns it: that module's own items are in scope unqualified,
and everything else it imports is available the way the module has it. So an
example writes `square(4)` — the way the module's own code does — and the same
example passes whether the library is tested on its own or through a consumer.
Documentation that stops being true fails the suite instead of quietly
misleading the next reader.
Lines beginning `# ` inside a block are hidden setup: they run, but they are
not part of what a reader sees.
## 10a. Calling C
`extern fn` declares a C function. There is no body and no binding layer: a
compiled build resolves the symbol at link time, and `grasm run` finds it in
its own process.
</code></pre>
<p>extern fn sqrt(x: f64) -> f64 extern fn getpid() -> i64</p>
<p>fn main() { let r = unsafe { sqrt(16.0) } print("{r}") }</p>
<pre><code><span class="ty">Parameters</span> <span class="kw">and</span> results are `<span class="ty">i64</span>` <span class="kw">and</span> `<span class="ty">f64</span>`<span class="op">.</span> `<span class="ty">bool</span>` is deliberately <span class="kw">not</span>
allowed<span class="op">:</span> <span class="ty">C</span>'s is one byte <span class="kw">and</span> <span class="ty">Grasm</span>'s is a register<span class="op">,</span> <span class="kw">and</span> that kind of mismatch
surfaces as garbage far from where it was caused<span class="op">.</span>
<span class="op">*</span><span class="op">*</span><span class="ty">It</span> has to be inside `<span class="kw">unsafe</span>`<span class="op">,</span> <span class="kw">and</span> it counts as both `<span class="kw">unsafe</span>` <span class="kw">and</span> `io`<span class="op">.</span><span class="op">*</span><span class="op">*</span>
<span class="ty">Calling</span> <span class="ty">C</span> is <span class="ty">Tier</span> <span class="nu">3</span> — outside everything the safe tiers guarantee — so the
source says so at the use<span class="op">,</span> <span class="kw">and</span> the package says so <span class="kw">in</span> its manifest<span class="op">:</span></code></pre>
<p>capabilities = ["io", "unsafe"]</p>
<pre><code><span class="ty">Naming</span> a <span class="ty">C</span> function without calling it needs `<span class="kw">unsafe</span>` as well<span class="op">:</span> holding the
function <span class="op">*</span>is<span class="op">*</span> the ability to call it<span class="op">.</span> <span class="ty">What</span> a <span class="ty">C</span> function does cannot be known<span class="op">,</span>
so the safe assumption is the only one available — an `extern` use counts
against the ceiling<span class="op">,</span> <span class="kw">and</span> a function that makes one cannot be handed to
`par_map`<span class="op">.</span> <span class="ty">A</span> consumer can see `<span class="kw">unsafe</span>` <span class="kw">in</span> your manifest<span class="op">,</span> transitively<span class="op">,</span> <span class="kw">and</span>
refuse to depend on you<span class="op">:</span> your <span class="ty">JSON</span> parser cannot quietly contain one<span class="op">.</span>
## 10b<span class="op">.</span> <span class="ty">Knowing</span> what you broke
`grasm api` prints a package's public surface — every `<span class="kw">pub</span>` item<span class="op">,</span> sorted<span class="op">,</span> one
line each<span class="op">.</span> <span class="ty">Save</span> it<span class="op">,</span> <span class="kw">and</span> a later `<span class="op">-</span><span class="op">-</span>against` says what changed<span class="op">:</span>
```text
$ grasm api src<span class="op">/</span>main<span class="op">.</span>gr <span class="op">></span> v1<span class="op">.</span>api
$ grasm api src<span class="op">/</span>main<span class="op">.</span>gr <span class="op">-</span><span class="op">-</span>against v1<span class="op">.</span>api
<span class="ty">CHANGED</span> lib<span class="op">::</span><span class="ty">Point</span>
was <span class="kw">type</span> <span class="ty">Point</span> <span class="op">=</span> <span class="op">{</span> x<span class="op">:</span> <span class="ty">i64</span><span class="op">,</span> y<span class="op">:</span> <span class="ty">i64</span> <span class="op">}</span>
now <span class="kw">type</span> <span class="ty">Point</span> <span class="op">=</span> <span class="op">{</span> x<span class="op">:</span> <span class="ty">f64</span><span class="op">,</span> y<span class="op">:</span> <span class="ty">i64</span> <span class="op">}</span>
breaking<span class="op">:</span> <span class="nu">0</span> removed<span class="op">,</span> <span class="nu">1</span> changed<span class="op">,</span> <span class="nu">0</span> added — this is a <span class="ty">MAJOR</span> release</code></pre>
<p>It exits 1 on a breaking change, so a build can refuse one that the version number does not admit to.</p>
<p>A version number is supposed to answer "will this break me". One a human typed cannot answer it — it records what somebody <em>believed</em>. This one is read off the code.</p>
<p>The limit is worth stating: this compares <strong>shape</strong>, not behaviour. A function that keeps its signature and starts returning something else is a <code>PATCH</code> here and a disaster for you. Deciding that automatically is undecidable, and a tool that claimed to would be worse than one that says where it stops.</p>
<h2 id="10c-making-a-failure-happen-twice">10c. Making a failure happen twice</h2>
<p>A program is a function of its code and everything it read from outside itself. The code is in the binary; the rest can be written down:</p>
<pre><code>$ GRASM_RECORD=j ./server # the run that went wrong
$ GRASM_REPLAY=j grasm run src/main.gr</code></pre>
<p>The second run does not read the clock, the disk, the network or the environment. It takes every answer from <code>j</code> — so it behaves the same on your laptop as it did on the machine that failed, as many times as you need, under a debugger.</p>
<p>It also does not <em>write</em>. Replaying a <code>write_file</code> to reproduce a bug would damage a disk. <code>print</code> is the deliberate exception: a replay whose output you cannot see is not worth running.</p>
<p><code>grasm journal</code> shows what was recorded:</p>
<pre><code>$ grasm journal j
j — 5 record(s), 264 bytes
1 now_millis 1787564958324
2 env Some("/home/dev")
3 read_file Ok("port = 8080\n")
4 tcp_listen Err(IoFailed("Permission denied (os error 13)"))
5 now_millis 1787564958324
5 call(s) across 4 builtin(s): env 1, now_millis 2, read_file 1, tcp_listen 1</code></pre>
<p><strong>Why this is cheap here and expensive elsewhere.</strong> Knowing which calls are non-deterministic is normally the hard part of building a record-and-replay tool. In Grasm it was already written down: a function that reaches outside the program declares an effect (§5), and one that does not <em>cannot</em>. The journal is "every call with a row, in order, and what it answered".</p>
<p><strong>Divergence is refused, not guessed.</strong> If the program takes a different path from the recorded one, the run stops and says which call disagreed:</p>
<pre><code>grasm: at call 3 the recording says `read_file` and this run asked for
`env` — the program has taken a different path from the one recorded, so
this is not a replay of it</code></pre>
<p>Handing back the next record anyway would produce something that looks like a replay and is not, and a tool whose whole promise is <em>this is what happened</em> must not be able to lie about it.</p>
<p>A recording is portable between engines, which is the case that matters: the failure happens in the compiled build and the debugger is in <code>grasm run</code>. It even replays in a browser tab — see replay.md.</p>
<h2 id="11-where-native-stops">11. Where <code>--native</code> stops</h2>
<p><code>grasm run</code> and <code>grasm build --native</code> are two implementations of one language, and <code>scripts/difftest.sh</code> holds them to identical output — and identical exit codes — on every program in the repository.</p>
<p>For most of this project's life the compiled backend was the <em>smaller</em> of the two, and this section was a list of shapes that ran under <code>grasm run</code> and could not be built. That list was seventeen entries long. It is one, and it is not a defect being worked off:</p>
<div class="scroll"><table><thead><tr><th>shape</th><th>why</th></tr></thead><tbody><tr><td>polymorphic recursion</td><td><code>fn f[T](x: T) -> i64 { f([x]) }</code> needs one compiled copy per type and the types never stop growing. No amount of work removes this; it is what monomorphization is, and it is refused by name at build time</td></tr></tbody></table></div>
<p>Everything else the interpreter accepts, the compiled backend compiles. <code>scripts/nativegaps.sh</code> holds that claim to 56 cases, running each under both engines and comparing output, exit code and leaks. Six of the fourteen generators in <code>scripts/</code> report how many of the programs they invented the backend turned away, and all six report <code>declined: 0</code> — and in all six a decline is a <em>failure</em> rather than a report. Every shape they generate is one the backend compiles, so a refusal is coverage lost rather than an honest no: it is either a new gap, which belongs in the table above, or a defect. Twice now a generator has counted one and exited 0, and both times the programs stopped running while the summary said nothing was wrong.</p>
<p>That is a statement about what has been checked, not a proof. A shape nobody has written is a shape nobody has tried. What has changed is the direction the gate points: it used to record what the backend could not do, and now it mostly records what it can.</p>
<p>A refusal, when one appears, is by name and at build time rather than a wrong answer. That is the promise, and it has been broken: a <code>fold</code> whose accumulator was fallible was named in this table as refused and was quietly miscompiled instead (F43). Both halves of that shape are in the gate list now — the one that works and the one that must not.</p>
<p>One row left this table by being answered rather than implemented. A constructor that leaves a type parameter undetermined — <code>Good(1)</code> for <code>Res[T, E]</code> — was refused here and accepted by the other two engines, which made it look like a backend limitation. [T-GENERIC-TYPE] says it is the language's rule, so the <em>checker</em> enforces it now and all three engines agree. A shape on a list of backend gaps is worth asking about twice: whether the backend should accept it, or whether the others should not.</p>
<h2 id="where-to-go-next">Where to go next</h2>
<ul><li><code>spec/</code> — the normative rules, each tagged and backed by a program in <code>spec/conformance/</code> that the test suite runs.</li><li><code>examples/</code> — runnable programs, including <code>stdlib_tour.gr</code> and <code>testing_tour.gr</code>.</li><li><code>projects/</code> — twelve larger programs, beginner through advanced.</li><li><code>docs/replay.md</code> — recording a run and replaying it, in full.</li><li><code>docs/wasm.md</code> — running a Grasm program in a browser tab.</li><li><code>MASTERPLAN.md</code> — where the language is going and why.</li></ul>
</main>
</div>
<footer class="sitefoot">
<div class="inner">
<div class="cols">
<div class="brandcol">
<a class="brand" href="index.html"><svg viewBox="0 0 48 48" width="20" height="20" fill="none" stroke="currentColor" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 6H39"/><path d="M24 14A13 13 0 1 0 37 27H26"/></svg><span>Grasm</span></a>
<span>Four engines, one answer.</span>
</div>
<div class="links">
<div><span>Start</span><a href="different.html">Why Grasm</a><a href="start.html">Getting started</a><a href="install.html">Install</a><a href="playground.html">Playground</a></div>
<div><span>Learn</span><a href="tutorial.html">Tutorial</a><a href="tour.html">Tour</a><a href="effects.html">Effects</a><a href="tiers.html">Ownership</a><a href="errors.html">Errors</a><a href="concurrency.html">Concurrency</a><a href="security.html">Security</a></div>
<div><span>Reference</span><a href="syntax.html">Syntax</a><a href="language.html">Language</a><a href="library.html">Library</a><a href="packages.html">Packages</a><a href="spec.html">Specification</a><a href="architecture.html">Architecture</a></div>
<div><span>More</span><a href="examples.html">Examples</a><a href="achieved.html">Scorecard</a><a href="performance.html">Performance</a><a href="about.html">About</a><a href="status.html">Status</a><a href="roadmap.html">Roadmap</a></div>
</div>
</div>
<p>Counts here are computed from the repository as the page is written;
measurements are read from the document that recorded them.</p>
</div>
</footer>
</body></html>