-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlang.txt
More file actions
3937 lines (2902 loc) · 102 KB
/
lang.txt
File metadata and controls
3937 lines (2902 loc) · 102 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
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# santa-lang Programming Language Specification
Version: 1.0
Authors: Edd Mann
Last Updated: 2025
## Table of Contents
1. [Introduction](#introduction)
2. [Lexical Structure](#lexical-structure)
3. [Type System](#type-system)
4. [Operators](#operators)
5. [Variables and Bindings](#variables-and-bindings)
6. [Expressions](#expressions)
7. [Control Flow](#control-flow)
8. [Functions](#functions)
9. [Pattern Matching](#pattern-matching)
10. [Collections](#collections)
11. [Built-in Functions](#built-in-functions)
12. [AOC Runner](#aoc-runner)
13. [External Functions](#external-functions)
14. [Semantics](#semantics)
15. [Implementation Notes](#implementation-notes)
16. [CLI Output Format Specification](#cli-output-format-specification)
---
## 1. Introduction
**Santa** is a functional, expression-oriented programming language designed specifically for solving Advent of Code puzzles. The language is influenced by Rust, Python, Clojure, F#, and Scala.
### Design Principles
- **Everything is an expression**: All constructs evaluate to a value
- **Everything is a function** (mostly): Operators and control structures are function-based
- **Semicolons or newlines separate statements**: Either semicolons or newlines can be used to separate statements; at least one is required between statements on the same line
- **Immutable by default**: Variables are immutable unless explicitly declared as mutable
- **Persistent data structures**: Collections are immutable; operations produce new collections
- **Expression blocks return implicitly**: The last expression in a block is its return value
### Language Philosophy
Santa prioritizes conciseness and expressiveness for algorithmic problem-solving, with a focus on functional programming patterns like mapping, filtering, folding, and lazy evaluation.
---
## 2. Lexical Structure
### 2.1 Character Set
Santa source files are UTF-8 encoded text.
### 2.2 Comments
Line comments start with `//` and continue to the end of the line.
```santa
// This is a full-line comment
let x = 42; // This is an inline comment
```
Comments are lexed as tokens but ignored during parsing.
### 2.3 Identifiers
Identifiers must match the pattern: `[a-zA-Z][a-zA-Z0-9_?]*`
Valid identifiers:
```
x
counter
parse_input
is_valid?
my_var_123
```
Identifiers may end with `?` by convention to indicate predicate functions (functions that return a boolean value). All built-in predicate functions follow this convention (e.g., `includes?`, `excludes?`, `any?`, `all?`). User-defined predicates should follow this convention.
### 2.4 Keywords
Reserved keywords:
```
let
mut
if
else
match
return
break
nil
true
false
```
### 2.5 Literals
#### Integer Literals
- 64-bit signed integers
- Decimal notation: `42`, `-17`, `0`
- Underscores for readability: `1_000_000`
#### Decimal Literals
- 64-bit floating-point (IEEE 754-2008 binary64)
- Decimal notation: `3.14`, `-0.5`
- Underscores for readability: `1_000.50`
#### String Literals
- UTF-8 encoded strings
- Delimited by double quotes: `"hello"`
- Escape sequences:
- `\n` - newline
- `\t` - tab
- `\r` - carriage return
- `\b` - backspace
- `\f` - form feed
- `\"` - double quote
- `\\` - backslash
```santa
"Hello, world!"
"Line 1\nLine 2"
"Tab\tseparated"
"Quote: \"text\""
"Windows\r\nline ending"
```
#### Boolean Literals
- `true` - Boolean true value
- `false` - Boolean false value
```santa
let is_valid = true;
let has_error = false;
```
Booleans are first-class values and can be used in comparisons, logical operations, and conditionals. Santa also supports truthy/falsy semantics for non-boolean values (see Section 14.1).
#### Nil Literal
`nil` represents the absence of a value.
---
## 3. Type System
Santa has a dynamic type system with the following types:
### 3.1 Integer
64-bit signed integers.
```santa
let x = 42;
let y = -17;
let big = 1_000_000;
```
### 3.2 Decimal
64-bit floating-point numbers (IEEE 754-2008 binary64).
```santa
let pi = 3.14159;
let small = 0.001;
let readable = 1_000.50;
```
### 3.3 String
UTF-8 encoded character sequences. String indexing is **grapheme-cluster indexed**, meaning each index refers to a visual character (grapheme cluster), not individual bytes or code points.
```santa
let greeting = "Hello";
let multiline = "Line 1\nLine 2";
let unicode = "❤🍕";
let emoji = "👨👩👧👦"; // Family emoji (single grapheme cluster)
```
String operations:
- Concatenation: `"hello" + " " + "world"`
- Repetition: `"abc" * 3` → `"abcabcabc"`
- Indexing: `"hello"[0]` → `"h"` (returns single grapheme as String)
- Slicing: `"hello"[1..=3]` → `"ell"`
- Complex Unicode: `"👨👩👧👦"[0]` → `"👨👩👧👦"` (entire family emoji)
### 3.4 Range
Ranges represent sequences of integers with lazy evaluation.
#### Exclusive Range (`start..end`)
From `start` (inclusive) to `end` (exclusive).
```santa
1..5 // 1, 2, 3, 4
5..1 // 5, 4, 3, 2 (auto-descending)
-2..2 // -2, -1, 0, 1
5..5 // [] (empty - start equals end)
```
#### Inclusive Range (`start..=end`)
From `start` to `end` (both inclusive).
```santa
1..=5 // 1, 2, 3, 4, 5
5..=1 // 5, 4, 3, 2, 1
-2..=2 // -2, -1, 0, 1, 2
5..=5 // [5] (single element - start equals end)
```
#### Unbounded Range (`start..`)
Infinite sequence starting from `start`, incrementing by 1.
```santa
1.. // 1, 2, 3, 4, ...
-5.. // -5, -4, -3, -2, ...
```
Ranges are lazy and can be used with collection functions:
```santa
1..10 |> map(_ * 2) // Lazy sequence of even numbers
1.. |> take(5) |> sum // Sum of first 5 positive integers
```
### 3.5 List
Ordered, heterogeneous, persistent sequences.
```santa
let numbers = [1, 2, 3, 4];
let mixed = [1, "two", 3.0];
let nested = [[1, 2], [3, 4]];
let empty = [];
```
Operations:
- Concatenation: `[1, 2] + [3, 4]` → `[1, 2, 3, 4]`
- Repetition: `[1, 2] * 3` → `[1, 2, 1, 2, 1, 2]`
- Indexing: `[10, 20, 30][1]` → `20`
- Negative indexing: `[10, 20, 30][-1]` → `30`
- Slicing: `[1, 2, 3, 4][1..=2]` → `[2, 3]`
### 3.6 Set
Unordered collections of unique elements (persistent). **Iteration order is not guaranteed** and may vary between program runs.
```santa
let unique = {1, 2, 3, 2, 1}; // {1, 2, 3}
let mixed = {"a", 1, 2.5};
let empty = {};
```
**Note on `{}` disambiguation**: The parser distinguishes between empty sets and empty blocks based on context:
- In **expression position** (assignment, function argument, list element): `{}` is parsed as an empty Set
- In **statement position** (if/else body, match arm body, function body): `{}` is parsed as an empty Block
```santa
let x = {}; // Empty set (expression position)
fold({}, |acc, x| ...) // Empty set (function argument)
if true { } // Empty block (statement position)
let f = || { }; // Empty block (function body)
```
Restrictions:
- Cannot contain Lazy Sequences
- Cannot contain Functions
Operations:
- Union: `{1, 2} + {2, 3}` → `{1, 2, 3}`
- Difference: `{1, 2, 3} - {2}` → `{1, 3}`
- Membership: `{1, 2, 3} \`includes?\` 2` → `true`
### 3.7 Dictionary
Unordered key-value mappings (persistent). **Iteration order is not guaranteed** and may vary between program runs.
```santa
let ages = #{"Alice": 30, "Bob": 25};
let mixed = #{1: "one", "two": 2};
let nested = #{"a": [1, 2], "b": #{3: 4}};
let empty = #{};
```
**Shorthand syntax**: When an identifier is used without a colon, it creates a key-value pair where the key is the identifier name as a string:
```santa
let name = "Alice";
let age = 30;
#{name, age} // Equivalent to #{"name": name, "age": age}
// Result: #{"name": "Alice", "age": 30}
```
Keys can be any type except Lazy Sequences and Functions.
Operations:
- Access: `ages["Alice"]` → `30`
- Missing keys: `ages["Charlie"]` → `nil`
- Association: `ages |> assoc("Charlie", 35)`
### 3.8 Lazy Sequence
Infinite sequences with deferred computation.
```santa
let evens = iterate(_ + 2, 0); // 0, 2, 4, 6, ...
let repeated = repeat(5); // 5, 5, 5, 5, ...
let cycled = cycle([1, 2, 3]); // 1, 2, 3, 1, 2, 3, ...
```
Lazy sequences are only computed when consumed by functions like `take`, `find`, or `reduce`.
### 3.9 Function
First-class functions.
```santa
let inc = |x| x + 1;
let add = |a, b| a + b;
```
Functions can be passed as arguments, returned from functions, and stored in collections.
### 3.10 Nil
Represents the absence of a value. Used when:
- Accessing non-existent collection elements
- Pattern matching fails
- If-expressions without else branches evaluate to false
- Functions have no explicit return value
```santa
[1, 2, 3][10] // nil
#{"a": 1}["b"] // nil
if false { 1 } // nil
```
### 3.11 Hashability
Some types can be used as Set elements and Dictionary keys (they are "hashable"). Others cannot.
| Type | Hashable? | Notes |
|------|-----------|-------|
| Nil | Yes | |
| Integer | Yes | |
| Decimal | Yes | |
| Boolean | Yes | |
| String | Yes | |
| List | Yes | Only if ALL elements are hashable |
| Set | Yes | |
| Dictionary | **No** | Cannot be used as key or set element |
| LazySequence | **No** | Cannot be used as key or set element |
| Function | **No** | Cannot be used as key or set element |
**Examples:**
```santa
{1, 2, 3} // OK: integers are hashable
{[1, 2], [3, 4]} // OK: lists of hashable elements
#{[1, 2]: "pair"} // OK: list key (elements are hashable)
{#{a: 1}} // Error: dictionaries are not hashable
{|x| x} // Error: functions are not hashable
```
**RuntimeErr:** Attempting to use a non-hashable value as a Set element or Dictionary key results in a runtime error.
---
## 4. Operators
### 4.1 Arithmetic Operators
**Type Coercion Rule**: For mixed-type arithmetic operations (Integer and Decimal), the **left operand determines the result type**. If the left operand is an Integer, the result is an Integer. If the left operand is a Decimal, the result is a Decimal.
```santa
1 + 2.5 // 3 (left is Integer, result is Integer)
1.5 + 2 // 3.5 (left is Decimal, result is Decimal)
```
#### Addition (`+`)
- **Integers/Decimals**: Numeric addition
- **Strings**: Concatenation (with type coercion when string is on the left)
- **Lists**: Concatenation
- **Sets**: Union
- **Dictionaries**: Merge (right precedence)
**String coercion**: When the left operand is a String, any right operand is coerced to a string. However, `Integer + String` is NOT supported (produces a runtime error). This asymmetric behavior matches the Rust and TypeScript implementations.
```santa
1 + 2 // 3
"hello" + " " + "world" // "hello world"
"count: " + 42 // "count: 42" (Integer coerced to String)
"value: " + 3.14 // "value: 3.14" (Decimal coerced to String)
// 42 + " items" // ERROR: Cannot add Integer and String
[1, 2] + [3, 4] // [1, 2, 3, 4]
{1, 2} + {2, 3} // {1, 2, 3}
#{a: 1} + #{b: 2} // #{a: 1, b: 2}
```
#### Subtraction (`-`)
- **Integers/Decimals**: Numeric subtraction
- **Sets**: Set difference
```santa
5 - 3 // 2
{1, 2, 3} - {2} // {1, 3}
```
#### Multiplication (`*`)
- **Integers/Decimals**: Numeric multiplication
- **Strings**: Repetition
- **Lists**: Repetition
```santa
3 * 4 // 12
"a" * 3 // "aaa"
[1, 2] * 2 // [1, 2, 1, 2]
```
#### Division (`/`)
- **Integers**: Floored division (Python-style, floors toward negative infinity)
- **Decimals**: Floating-point division
```santa
7 / 2 // 3
-7 / 2 // -4 (floors toward negative infinity)
7 / -2 // -4
-7 / -2 // 3
7.0 / 2 // 3.5
-7.0 / 2 // -3.5
```
#### Modulo (`%`)
- **Integers**: Remainder of division using floored division (Python-style)
- The result has the same sign as the divisor (right operand)
```santa
7 % 3 // 1
-7 % 3 // 2 (floor: -7 = -3*3 + 2)
7 % -3 // -2 (floor: 7 = -2*-3 + (-2))
-7 % -3 // -1
```
### 4.2 Comparison Operators
Available for comparable types: `<`, `>`, `<=`, `>=`
**Comparable types**: Only Integer, Decimal, and String types support comparison operators. Comparing other types (List, Set, Dictionary, Function, Lazy Sequence) results in a runtime error.
```santa
5 < 10 // true
3.14 < 5 // true (mixed numeric comparison works)
"abc" < "def" // true (lexicographic)
[1, 2] < [1, 3] // Error: Lists are not comparable
```
### 4.3 Equality Operators
- `==` - Equality
- `!=` - Inequality
Equality is structural for all types:
```santa
1 == 1 // true
[1, 2] == [1, 2] // true
#{a: 1} == #{a: 1} // true
{1, 2} == {2, 1} // true (sets are unordered)
```
### 4.4 Logical Operators
#### Logical NOT (`!`)
Prefix operator that returns the logical negation based on truthiness.
```santa
!true // false
!false // true
!0 // true (0 is falsy)
!1 // false (1 is truthy)
!"" // true (empty string is falsy)
!"hello" // false (non-empty string is truthy)
![] // true (empty list is falsy)
!nil // true (nil is falsy)
```
#### Logical OR (`||`)
Returns `true` if either operand is truthy, `false` otherwise. Short-circuit evaluation: if the left operand is truthy, the right operand is not evaluated.
```santa
true || false // true
1 || 0 // true (1 is truthy)
0 || 2 // true (2 is truthy)
false || nil // false (both are falsy)
false || 0 // false (both are falsy)
```
#### Logical AND (`&&`)
Returns `true` if both operands are truthy, `false` otherwise. Short-circuit evaluation: if the left operand is falsy, the right operand is not evaluated.
```santa
true && true // true
1 && 2 // true (both are truthy)
0 && 2 // false (0 is falsy)
1 && nil // false (nil is falsy)
```
### 4.5 Bitwise Operators
- `bit_and(a, b)` - Bitwise AND
- `bit_or(a, b)` - Bitwise OR
- `bit_xor(a, b)` - Bitwise XOR
- `bit_not(value)` - Bitwise NOT (complement)
- `bit_shift_left(value, shift)` - Left shift
- `bit_shift_right(value, shift)` - Right shift
```santa
bit_and(12, 10) // 8 (1100 & 1010 = 1000)
bit_or(12, 10) // 14 (1100 | 1010 = 1110)
bit_xor(12, 10) // 6 (1100 ^ 1010 = 0110)
bit_not(12) // -13 (bitwise complement)
bit_shift_left(1, 3) // 8 (1 << 3 = 1000)
bit_shift_right(8, 2) // 2 (1000 >> 2 = 10)
```
### 4.6 Indexing Operator
Square brackets `[]` for collection access.
**Lists:**
```santa
[10, 20, 30][1] // 20
[10, 20, 30][-1] // 30 (from end)
[10, 20, 30][1..=2] // [20, 30] (slice)
```
**Strings:**
```santa
"hello"[0] // "h"
"hello"[-1] // "o"
"hello"[1..=3] // "ell"
```
**Dictionaries:**
```santa
#{a: 1, b: 2}["a"] // 1
#{a: 1}["c"] // nil
```
### 4.7 Pipeline Operator (`|>`)
Thread a value through a series of function calls.
```santa
[1, 2, 3]
|> map(_ * 2)
|> filter(_ > 2)
|> sum
// Equivalent to: sum(filter(_ > 2, map(_ * 2, [1, 2, 3])))
```
### 4.8 Function Composition Operator (`>>`)
Forward composition: applies functions left-to-right.
```santa
let parse = lines >> map(int) >> sum;
// Equivalent to: |x| sum(map(int, lines(x)))
// Applies lines first, then map(int), then sum
parse("1\n2\n3") // 6
let inc_then_double = _ + 1 >> _ * 2;
inc_then_double(5) // 12 (applies +1 first, then *2)
```
### 4.9 Spread Operator (`..`)
Spread collections or create rest parameters.
```santa
let list = [1, 2, 3];
[0, ..list, 4] // [0, 1, 2, 3, 4]
let fn = |first, ..rest| rest;
fn(1, 2, 3, 4) // [2, 3, 4]
```
---
## 5. Variables and Bindings
### 5.1 Variable Declaration
Variables are declared with `let`:
```santa
let x = 42;
let name = "Alice";
let values = [1, 2, 3];
```
### 5.2 Immutability
Variables are immutable by default:
```santa
let x = 1;
x = 2; // Error: Variable 'x' is not mutable
```
### 5.3 Mutable Variables
Use `let mut` for mutable variables:
```santa
let mut counter = 0;
counter = counter + 1; // OK
counter = 5; // OK
```
### 5.4 Destructuring
#### List Destructuring
**Basic destructuring:**
```santa
let [a, b, c] = [1, 2, 3];
// a = 1, b = 2, c = 3
```
**Rest patterns** (can appear anywhere in the pattern):
```santa
let [first, ..rest] = [1, 2, 3, 4];
// first = 1, rest = [2, 3, 4]
let [..initial, last] = [1, 2, 3, 4];
// initial = [1, 2, 3], last = 4
let [first, ..middle, last] = [1, 2, 3, 4, 5];
// first = 1, middle = [2, 3, 4], last = 5
```
**Ignore patterns** (using `_`):
```santa
let [x, _, z] = [1, 2, 3];
// x = 1, z = 3 (ignore middle element)
let [a, b, c, ..] = [1, 2, 3, 4, 5];
// a = 1, b = 2, c = 3 (ignore rest)
```
**Nested destructuring:**
```santa
let [[a, b], c] = [[1, 2], 3];
// a = 1, b = 2, c = 3
let [x, [y, z]] = [1, [2, 3]];
// x = 1, y = 2, z = 3
let [[a, ..rest], last] = [[1, 2, 3], 4];
// a = 1, rest = [2, 3], last = 4
```
**Placeholder `_` contexts:**
The underscore `_` has multiple context-dependent meanings in Santa:
1. **Pattern matching**: Ignore/discard a value: `let [x, _] = [1, 2]`
2. **Partial application**: Placeholder for arguments: `let inc = _ + 1`
3. **Function parameters**: Ignored parameter: `|_| 42`
4. **Match wildcard**: Catch-all pattern: `match x { _ { "default" } }`
#### Mutable Destructuring
```santa
let mut [x, y] = [1, 2];
x = 10;
y = 20;
```
### 5.5 Shadowing
Variables can be shadowed in nested scopes:
```santa
let x = 1;
{
let x = 2;
x; // 2
};
x; // 1
```
---
## 6. Expressions
### 6.1 Literal Expressions
Literals evaluate to themselves:
```santa
42
3.14
"hello"
[1, 2, 3]
#{a: 1, b: 2}
```
### 6.2 Variable Expressions
Identifiers evaluate to their bound values:
```santa
let x = 42;
x // 42
```
### 6.3 Block Expressions
Blocks group expressions and return the last expression:
```santa
let result = {
let x = 10;
let y = 20;
x + y
};
result // 30
```
**Let-bindings are expressions** that return the bound value:
```santa
let value = {
let x = 10;
let y = 20;
};
value // 20 (let y = 20 returns 20)
let a = (let b = 5);
a // 5
```
Empty blocks return `nil`.
### 6.4 Function Call Expressions
```santa
max(1, 2, 3)
split(",", "a,b,c")
```
### 6.5 Infix Function Calls
Functions can be called in infix position using backticks:
```santa
[1, 2, 3] `includes?` 2
// Equivalent to: includes?([1, 2, 3], 2)
"hello,world" `split` ","
// Equivalent to: split(",", "hello,world")
```
### 6.6 Method-Style Calls (Pipeline)
The pipeline operator enables method-style chaining:
```santa
input
|> lines
|> map(int)
|> filter(_ > 0)
|> sum
```
---
## 7. Control Flow
### 7.1 If Expressions
Conditional branching. Returns a value.
```santa
if condition { expression }
if condition {
expression1
} else {
expression2
}
```
**Examples:**
```santa
let x = if 5 > 3 { "yes" } else { "no" }; // "yes"
let y = if false { 1 }; // nil (no else branch)
// Using match (more declarative)
let grade = |score| match score {
90.. => "A",
80..90 => "B",
70..80 => "C",
_ => "F"
};
// With let-binding in condition
if let x = compute() {
use(x)
} else {
default()
}
```
**Truthy Semantics:**
The condition is evaluated for truthiness (see Section 14.1).
### 7.2 Match Expressions
Pattern matching with multiple branches.
```santa
match subject {
pattern1 { expression1 }
pattern2 { expression2 }
pattern3 { expression3 }
}
```
Returns the value of the matching branch, or `nil` if no match.
**Primitive Matching:**
```santa
let describe = |n| match n {
0 { "zero" }
1 { "one" }
2 { "two" }
_ { "many" }
};
```
**List Pattern Matching:**
```santa
match list {
[] { "empty" }
[x] { "single element" }
[x, y] { "two elements" }
[head, ..tail] { "multiple elements" }
}
```
**Range Matching:**
```santa
match value {
0..5 { "less than 5" }
5..=10 { "between 5 and 10" }
11.. { "greater than 10" }
}
```
**Guard Clauses:**
```santa
match list {
[x] if x > 10 { "single large element" }
[x, y] if x == y { "two equal elements" }
_ { "other" }
}
```
### 7.3 Return Statement
Early return from a function. **Context**: `return` is only valid inside function bodies (including lambdas). Using `return` outside a function results in a runtime error.
```santa
let check = |x| {
if x < 0 { return "negative" }
if x == 0 { return "zero" }
"positive"
};
return 10; // Error: return outside function
```
### 7.4 Break Statement
Break from an iteration context. **Context**: `break` is only valid inside iteration constructs (`reduce`, `fold`, `each`, lazy sequence consumers). Using `break` outside these contexts results in a runtime error.
```santa
1.. |> reduce |acc, n| {
if n > 10 { break acc }
else { acc + n }
}
// Sum of 1..10 = 55
let x = break 5; // Error: break outside iteration
```
Break can return a value that becomes the result of the enclosing expression.
---
## 8. Functions
### 8.1 Function Definition
Anonymous function syntax:
```santa
|param1, param2, ...| expression
|param1, param2, ...| {
// statements
expression
}
```
Bind to a variable:
```santa
let add = |a, b| a + b;
let greet = |name| "Hello, " + name;
```
### 8.2 Function Invocation
```santa
add(1, 2) // 3
greet("Alice") // "Hello, Alice"
```
### 8.3 Closures
Functions can capture variables from enclosing scopes:
```santa
let make_adder = |x| {
|y| x + y
};
let add5 = make_adder(5);
add5(3) // 8
```
Closures can capture mutable variables:
```santa
let counter = || {
let mut count = 0;
|| {
count = count + 1;
count
}
}();
counter() // 1
counter() // 2
counter() // 3
```
### 8.4 Partial Application
Using the placeholder `_`:
```santa
let inc = _ + 1;
let dec = _ - 1;
let double = _ * 2;
inc(5) // 6
dec(10) // 9
double(4) // 8
```
Multiple arguments:
```santa
let subtract_from_10 = 10 - _;
subtract_from_10(3) // 7
let divide = _ / _;
divide(10, 2) // 5
```
Partial application with explicit function syntax:
```santa
let inc = +(1); // |x| x + 1
let dec = -(_, 1); // |x| x - 1
```
### 8.5 Variadic Functions (Rest Parameters)
```santa
let sum_all = |..nums| nums |> sum;
sum_all(1, 2, 3, 4) // 10