@mojir/lits 2.1.35 → 2.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # Lits
2
2
 
3
- Lits is a lexically scoped pure functional language with algebraic notation. It combines the power of functional programming with an intuitive, readable syntax that makes complex operations simple and expressive.
3
+ A functional language with algebraic notation and JavaScript interoperability.
4
4
 
5
5
  Try it in the [Lits Playground](https://mojir.github.io/lits/).
6
6
 
7
7
  ## Features
8
8
 
9
9
  - **Pure functional language** - Variables cannot be changed, ensuring predictable behavior and easier reasoning about code
10
+ - **Expression-based syntax** - Everything in Lits is an expression that returns a value; there are no statements, making the language highly composable and consistent
11
+ - **Fully serializable** - Every value returned from Lits evaluation, including functions and regexps, is serializable as JSON
10
12
  - **JavaScript interoperability** - JavaScript values and functions can easily be exposed in Lits
11
13
  - **First-class functions** - Functions are treated as values that can be passed to other functions
12
14
  - **Algebraic notation** - All operators can be used as functions, and functions that take two parameters can be used as operators
@@ -56,7 +58,7 @@ The REPL provides an interactive environment where you can experiment with Lits
56
58
  Here's a simple example to get you started:
57
59
 
58
60
  ```lits
59
- // Defining a function
61
+ // Defining a function - note that everything returns a value
60
62
  let square = x -> x * x;
61
63
 
62
64
  // Using the function
@@ -68,10 +70,33 @@ let squares = [1, 2, 3, 4, 5] map square;
68
70
  // => [1, 4, 9, 16, 25]
69
71
 
70
72
  // Using operator as a function
71
- let sum = +([1, 2, 3, 4, 5]);
73
+ +(1, 2, 3, 4, 5);
72
74
  // => 15
73
75
  ```
74
76
 
77
+ ## Expression-Based Language
78
+
79
+ In Lits, everything is an expression that evaluates to a value. This means:
80
+
81
+ ```lits
82
+ // Conditional expressions always return a value
83
+ let a = 10;
84
+ let result = if a > 0 then "positive" else "non-positive" end;
85
+
86
+ // Function definitions are expressions that return the function
87
+ let add = (a, b) -> a + b;
88
+
89
+ // Even variable bindings return the bound value
90
+ let x = let y = 5; // x becomes 5
91
+
92
+ // Blocks are expressions - they return the last expression's value
93
+ let value = {
94
+ let temp = 42;
95
+ temp * 2 + 1 // => 85
96
+ };
97
+ ```
98
+ This expression-based design makes Lits highly composable and eliminates the statement/expression distinction found in many other languages.
99
+
75
100
  ## Basic Syntax
76
101
 
77
102
  ### Data Types
@@ -87,24 +112,231 @@ let sum = +([1, 2, 3, 4, 5]);
87
112
 
88
113
  // Strings
89
114
  "Hello, world!";
115
+ "String with \"escapes\"";
90
116
 
91
117
  // Booleans
92
118
  true;
93
119
  false;
94
120
 
95
- // Null
121
+ // Functions
122
+ (x -> x * 2); // Anonymous function
123
+ let add = (a, b) -> a + b; // Named function
124
+
125
+ // Regular expressions
126
+ #"[a-z]+";
127
+ #"\d{3}-\d{3}-\d{4}";
128
+
129
+ // null
96
130
  null;
131
+ ```
97
132
 
98
- // Arrays
99
- [1, 2, 3, 4];
133
+ #### Arrays (General Collections)
134
+
135
+ Arrays are the primary collection type in Lits, supporting mixed data types:
136
+
137
+ ```lits
138
+ // Basic arrays
139
+ [1, 2, 3, 4, 5];
140
+ ["apple", "banana", "orange"];
141
+ [true, 42, "mixed types"];
142
+
143
+ // Nested arrays
144
+ [[1, 2], [3, 4], [5, 6]];
145
+ [{name: "Alice"}, {name: "Bob"}];
146
+
147
+ // Array operations
148
+ let numbers = [1, 2, 3, 4, 5];
149
+ numbers[0]; // => 1 (indexing)
150
+ count(numbers); // => 5 (length)
151
+ first(numbers); // => 1
152
+ last(numbers); // => 5
153
+ rest(numbers); // => [2, 3, 4, 5]
154
+
155
+ // Functional array operations
156
+ numbers map -> $ * 2; // => [2, 4, 6, 8, 10]
157
+ numbers filter odd?; // => [1, 3, 5]
158
+ ```
159
+
160
+ #### Vectors (Number Arrays)
100
161
 
101
- // Objects
162
+ A vector is simply a non-empty array containing only numbers. The `vec:` namespace provides mathematical operations specifically for these number arrays:
163
+
164
+ ```lits
165
+ // Vectors are just number arrays
166
+ [1, 2, 3, 4, 5]; // This is a vector
167
+ [3.14, 2.71, 1.41]; // This is also a vector
168
+ [1, "hello", 3]; // This is NOT a vector (mixed types)
169
+ []; // This is NOT a vector (empty)
170
+
171
+ // Vector creation functions
172
+ vec:zeros(5); // => [0, 0, 0, 0, 0]
173
+ vec:ones(3); // => [1, 1, 1]
174
+ vec:linspace(0, 10, 5); // => [0, 2.5, 5, 7.5, 10]
175
+ vec:fill(4, 3.14); // => [3.14, 3.14, 3.14, 3.14]
176
+ vec:generate(5, -> $ * 2); // => [0, 2, 4, 6, 8]
177
+
178
+ // Vector mathematical operations (use lin: namespace for vector math)
179
+ lin:dot([1, 2, 3], [4, 5, 6]); // => 32 (dot product)
180
+ lin:euclidean-norm([3, 4]); // => 5.0 (Euclidean norm/magnitude)
181
+ lin:normalize([3, 4]); // => [0.6, 0.8] (unit vector)
182
+ lin:euclidean-distance([0, 0], [3, 4]); // => 5.0 (Euclidean distance)
183
+
184
+ // Vector statistical operations
185
+ vec:sum([1, 2, 3, 4]); // => 10
186
+ vec:mean([1, 2, 3, 4]); // => 2.5
187
+ vec:median([1, 2, 3, 4, 5]); // => 3
188
+ vec:stdev([1, 2, 3, 4]); // => 1.29... (standard deviation)
189
+ vec:variance([1, 2, 3, 4]); // => 1.67... (variance)
190
+
191
+ // Vector analysis
192
+ vec:min([3, 1, 4, 1, 5]); // => 1
193
+ vec:max([3, 1, 4, 1, 5]); // => 5
194
+ vec:min-index([3, 1, 4]); // => 1 (index of minimum)
195
+ vec:max-index([3, 1, 4]); // => 2 (index of maximum)
196
+
197
+ // Cumulative operations
198
+ vec:cumsum([1, 2, 3, 4]); // => [1, 3, 6, 10]
199
+ vec:cumprod([1, 2, 3, 4]); // => [1, 2, 6, 24]
200
+
201
+ // Vector predicates
202
+ vec:increasing?([1, 1, 2, 3, 4]); // => true
203
+ vec:strictly-increasing?([1, 1, 2, 3, 4]); // => true
204
+
205
+ // Structural equality works with all vectors
206
+ [1, 2, 3] == [1, 2, 3]; // => true
207
+ [1, 2] == [1, 2, 3]; // => false
208
+ ```
209
+
210
+ #### Matrices (2D Vectors)
211
+
212
+ A matrix is a 2D array where each row is a vector (non-empty array of numbers) and all rows have the same length. The `mat:` namespace provides linear algebra operations for these structures:
213
+
214
+ ```lits
215
+ // Matrices are 2D number arrays with consistent row lengths
216
+ [[1, 2], [3, 4]]; // This is a 2x2 matrix
217
+ [[1, 2, 3], [4, 5, 6]]; // This is a 2x3 matrix
218
+ [[1, 2], [3, 4, 5]]; // This is NOT a matrix (inconsistent row length)
219
+ [[1, "hello"], [3, 4]]; // This is NOT a matrix (contains non-numbers)
220
+ [[]]; // This is NOT a matrix (contains empty row)
221
+
222
+ // Basic matrix operations
223
+ let matrixA = [[1, 2], [3, 4]];
224
+ let matrixB = [[5, 6], [7, 8]];
225
+
226
+ mat:mul(matrixA, matrixB); // => [[19, 22], [43, 50]] (multiplication)
227
+ mat:det(matrixA); // => -2 (determinant)
228
+ mat:inv(matrixA); // => [[-2, 1], [1.5, -0.5]] (inverse)
229
+ mat:trace(matrixA); // => 5 (trace - sum of diagonal)
230
+
231
+ // Matrix construction
232
+ mat:hilbert(3); // => 3x3 Hilbert matrix
233
+ mat:vandermonde([1, 2, 3]); // => Vandermonde matrix from vector
234
+ mat:band(4, 1, 1); // => 4x4 band matrix
235
+
236
+ // Matrix properties and predicates
237
+ mat:symmetric?([[1, 2], [2, 1]]); // => true
238
+ mat:invertible?([[1, 2], [3, 4]]); // => true
239
+ mat:square?([[1, 2], [3, 4]]); // => true
240
+ mat:diagonal?([[1, 0], [0, 2]]); // => true
241
+ mat:identity?([[1, 0], [0, 1]]); // => true
242
+
243
+ // Advanced matrix operations
244
+ mat:adj(matrixA); // => [[4, -2], [-3, 1]] (adjugate)
245
+ mat:cofactor(matrixA); // => cofactor matrix
246
+ mat:minor(matrixA, 0, 1); // => minor by removing row 0, col 1
247
+ mat:frobenius-norm(matrixA); // => Frobenius norm
248
+ mat:1-norm(matrixA); // => 1-norm (max column sum)
249
+ mat:inf-norm(matrixA); // => infinity norm (max row sum)
250
+ mat:max-norm(matrixA); // => max norm (largest absolute element)
251
+
252
+ // Matrix analysis
253
+ mat:rank(matrixA); // => matrix rank
254
+ ```
255
+
256
+ #### Objects (Maps)
257
+
258
+ Objects store key-value pairs:
259
+
260
+ ```lits
261
+ // Object creation
102
262
  { name: "John", age: 30 };
263
+ { "key with spaces": "value", count: 42 };
103
264
 
104
- // Regular expressions
105
- #"pattern";
265
+ // Nested objects
266
+ {
267
+ person: { name: "Alice", age: 25 },
268
+ scores: [95, 87, 92],
269
+ active: true
270
+ };
271
+
272
+ // Object operations
273
+ let user = { name: "Bob", age: 30, city: "NYC" };
274
+ get(user, "name"); // => "Bob"
275
+ assoc(user, "age", 31); // => new object with age updated
276
+ dissoc(user, "city"); // => new object without city
277
+ keys(user); // => ["name", "age", "city"]
278
+ vals(user); // => ["Bob", 30, "NYC"]
279
+ ```
280
+
281
+ #### Type Predicates
282
+
283
+ Lits provides predicate functions to check data types at runtime:
284
+
285
+ ```lits
286
+ // Basic type predicates
287
+ number?(42); // => true
288
+ string?("hello"); // => true
289
+ boolean?(true); // => true
290
+ function?(x -> x * 2); // => true
291
+ regexp?(#"[a-z]+"); // => true
292
+ array?([1, 2, 3]); // => true
293
+ object?({name: "Alice"}); // => true
294
+ null?(null); // => true
295
+
296
+ // Specialized array predicates
297
+ vector?([1, 2, 3]); // => true (non-empty number array)
298
+ vector?([1, "hello", 3]); // => false (mixed types)
299
+ vector?([]); // => false (empty)
300
+
301
+ matrix?([[1, 2], [3, 4]]); // => true (2D number array, consistent rows)
302
+ matrix?([[1, 2], [3]]); // => false (inconsistent row lengths)
303
+ matrix?([[]]); // => false (contains empty row)
304
+
305
+ // Collection predicates
306
+ seq?([1, 2, 3]); // => true (sequences: strings and arrays)
307
+ seq?("hello"); // => true
308
+ seq?({a: 1}); // => false
309
+
310
+ coll?([1, 2, 3]); // => true (collections: strings, arrays, objects)
311
+ coll?("hello"); // => true
312
+ coll?({a: 1}); // => true
313
+ coll?(42); // => false
314
+ ```
315
+
316
+ #### Type Hierarchy
317
+
318
+ The type predicates follow a logical hierarchy:
319
+
320
+ ```lits
321
+ // If something is a matrix, it's also a vector and an array
322
+ let mat = [[1, 2], [3, 4]];
323
+ matrix?(mat); // => true
324
+ vector?(mat); // => true (matrix is a special vector)
325
+ array?(mat); // => true (vector is a special array)
326
+
327
+ // If something is a vector, it's also an array
328
+ let vec = [1, 2, 3];
329
+ vector?(vec); // => true
330
+ array?(vec); // => true
331
+
332
+ // But not all arrays are vectors
333
+ let arr = [1, "hello", 3];
334
+ array?(arr); // => true
335
+ vector?(arr); // => false (contains non-numbers)
106
336
  ```
107
337
 
338
+ Each data type is immutable by design - operations return new values rather than modifying existing ones, ensuring predictable behavior and easier reasoning about code.
339
+
108
340
  ### Mathematical Constants
109
341
 
110
342
  Lits provides predefined mathematical constants:
@@ -153,37 +385,130 @@ let y = {
153
385
 
154
386
  #### Destructuring
155
387
 
388
+ ##### Basic Object Destructuring
389
+
156
390
  ```lits
157
391
  // Object destructuring
158
392
  let { name, age } = { name: "John", age: 30 };
159
393
  // name => "John", age => 30
160
394
  ```
161
395
 
396
+ ```lits
397
+ // With default values
398
+ let { name = "Unknown", age = 0 } = { name: "John" };
399
+ // name => "John", age => 0
400
+ ```
401
+
402
+ ```lits
403
+ // Renaming with 'as'
404
+ let { name as userName } = { name: "Dave" };
405
+ // userName => "Dave"
406
+ ```
407
+
408
+ ```lits
409
+ // Multiple renames
410
+ let { firstName as name, age as years } = { firstName: "Eve", age: 28 };
411
+ // name => "Eve", years => 28
412
+ ```
413
+
414
+ ##### Advanced Destructuring Patterns
415
+
416
+ ```lits
417
+ // Complex nested destructuring with defaults and renaming
418
+ let {
419
+ name as userName = "Guest",
420
+ profile: {
421
+ age = 0,
422
+ contact: { email as userEmail = "none" }
423
+ },
424
+ settings = { theme: "light" },
425
+ scores as userScores = [],
426
+ ...others
427
+ } = { name: "Sam", profile: { contact: {} } };
428
+ // userName => "Sam", age => 0, userEmail => "none", etc.
429
+ ```
430
+
431
+ ```lits
432
+ // Combining array and object destructuring
433
+ let [{ name }, { age }] = [{ name: "Tina" }, { age: 33 }];
434
+ // name => "Tina", age => 33
435
+ ```
436
+
437
+ ```lits
438
+ // Object with array property destructuring
439
+ let { name, scores: [one, two] } = { name: "Uma", scores: [85, 92] };
440
+ // name => "Uma", one => 85, two => 92
441
+ ```
442
+
443
+ ##### Array Destructuring
444
+
162
445
  ```lits
163
446
  // Array destructuring
164
- let [a, b] = [1, 2, 3, 4];
165
- // a => 1, b => 2
447
+ let [, , a, b] = [1, 2, 3, 4];
448
+ // a => 3, b => 4
166
449
  ```
167
450
 
168
451
  ```lits
169
- // With default values
170
- let { name = "Unknown", age = 0 } = { name: "John" };
171
- // name => "John", age => 0
452
+ // Array destructuring with defaults
453
+ let [one, two = 2] = [1];
454
+ // one => 1, two => 2
455
+ ```
456
+
457
+ ```lits
458
+ // Skipping elements
459
+ let [x, , z] = [1, 2, 3];
460
+ // x => 1, z => 3
172
461
  ```
173
462
 
463
+ ##### Rest Patterns
464
+
174
465
  ```lits
175
- // Rest patterns
466
+ // Array rest pattern
176
467
  let [head, ...tail] = [1, 2, 3, 4];
177
468
  // head => 1, tail => [2, 3, 4]
178
469
  ```
179
470
 
180
471
  ```lits
181
- // Destructuring in function parameters
182
- let displayPerson = ({name, age}) ->
183
- name ++ " is " ++ str(age) ++ " years old";
472
+ // Object rest pattern
473
+ let { name, ...otherProps } = { name: "John", age: 30, city: "NYC" };
474
+ // name => "John", otherProps => { age: 30, city: "NYC" }
475
+ ```
476
+
477
+ ```lits
478
+ // Empty rest patterns
479
+ let [only, ...empty] = [1];
480
+ // only => 1, empty => []
481
+ ```
482
+
483
+ ##### Function Parameter Destructuring
484
+
485
+ ```lits
486
+ // Basic parameter destructuring
487
+ let greet = ({ name }) -> "Hello, " ++ name;
488
+ greet({ name: "Pat" });
489
+ // => "Hello, Pat"
490
+ ```
491
+
492
+ ```lits
493
+ // With defaults in parameters
494
+ let greet2 = ({ name = "friend" }) -> "Hello, " ++ name;
495
+ greet2({});
496
+ // => "Hello, friend"
497
+ ```
498
+
499
+ ```lits
500
+ // Nested parameter destructuring
501
+ let processUser = ({ profile: { name, age }}) ->
502
+ name ++ " is " ++ str(age);
503
+ processUser({ profile: { name: "Quinn", age: 29 }});
504
+ // => "Quinn is 29"
505
+ ```
184
506
 
185
- displayPerson({ name: "John", age: 30 });
186
- // => "John is 30 years old"
507
+ ```lits
508
+ // Array parameter destructuring
509
+ let processCoords = ([x, y]) -> x + y;
510
+ processCoords([3, 4]);
511
+ // => 7
187
512
  ```
188
513
 
189
514
  ### Functions
@@ -220,55 +545,80 @@ let factorial = n ->
220
545
  #### If/Unless
221
546
 
222
547
  ```lits
223
- // If expression
224
- let x = !:random-int(0, 20); // Random number between 0 and 19
548
+ let x = 15; // Fixed value for compilation
225
549
 
226
550
  if x > 10 then
227
551
  "large"
228
552
  else
229
553
  "small"
230
554
  end;
231
- // => "large" (if x > 10) or "small" (if x <= 10)
555
+ // => "large"
232
556
 
233
557
  // If without else returns null
234
558
  if false then "never" end;
235
559
  // => null
236
560
 
237
561
  // Unless (inverted if)
238
- unless x > 10 then
562
+ let y = 8;
563
+ unless y > 10 then
239
564
  "small"
240
565
  else
241
566
  "large"
242
567
  end;
243
- // => "small" (if x <= 10) or "large" (if x > 10)
568
+ // => "small"
244
569
  ```
245
570
 
246
571
  #### Cond
247
572
 
248
573
  ```lits
249
- let x = !:random-int(0, 20); // Random number between 0 and 19
574
+ let x = 12;
250
575
 
251
576
  // Multi-branch conditional
252
577
  cond
253
578
  case x < 5 then "small"
254
579
  case x < 10 then "medium"
255
580
  case x < 15 then "large"
256
- end ?? "extra large"
257
- // Tests conditions sequentially, returns first truthy match
581
+ case true then "extra large" // default case
582
+ end;
583
+ // => "large"
584
+
585
+ // Cond with complex conditions
586
+ let urgent = true;
587
+ let important = false;
588
+ let priority = cond
589
+ case urgent && important then "critical"
590
+ case urgent then "high"
591
+ case important then "medium"
592
+ case true then "low"
593
+ end;
594
+ // => "high"
258
595
  ```
259
596
 
260
597
  #### Switch
261
598
 
262
599
  ```lits
263
- let x = !:random-int(0, 3); // Random number between 0 and 2
600
+ let x = 1;
264
601
 
265
602
  // Switch on value
266
603
  switch x
267
604
  case 0 then "zero"
268
605
  case 1 then "one"
269
606
  case 2 then "two"
270
- end
271
- // => "zero" (if x = 0), "one" (if x = 1), etc., or null if no match
607
+ end;
608
+ // => "one"
609
+
610
+ // Switch with multiple cases
611
+ let userInput = "help";
612
+ let exit = () -> "exiting";
613
+ let showHelp = () -> "showing help";
614
+ let saveData = () -> "saving data";
615
+
616
+ switch userInput
617
+ case "quit" then exit()
618
+ case "help" then showHelp()
619
+ case "save" then saveData()
620
+ end;
621
+ // => "showing help"
272
622
  ```
273
623
 
274
624
  ### Loops and Iteration
@@ -280,15 +630,15 @@ end
280
630
  for (x in [1, 2, 3, 4]) -> x * 2;
281
631
  // => [2, 4, 6, 8]
282
632
 
283
- // With filtering
633
+ // With filtering (when clause)
284
634
  for (x in [1, 2, 3, 4] when odd?(x)) -> x * 2;
285
635
  // => [2, 6]
286
636
 
287
- // With while condition
637
+ // With early termination (while clause)
288
638
  for (x in [1, 2, 3, 4] while x < 3) -> x * 2;
289
639
  // => [2, 4]
290
640
 
291
- // With let bindings
641
+ // With let bindings for intermediate calculations
292
642
  for (x in [1, 2, 3] let doubled = x * 2) -> doubled + 1;
293
643
  // => [3, 5, 7]
294
644
 
@@ -296,6 +646,20 @@ for (x in [1, 2, 3] let doubled = x * 2) -> doubled + 1;
296
646
  for (x in [1, 2], y in [10, 20]) -> x + y;
297
647
  // => [11, 21, 12, 22]
298
648
 
649
+ // Complex for comprehensions with multiple conditions
650
+ for (
651
+ i in range(10)
652
+ let ii = i ^ 2
653
+ while ii < 40
654
+ when ii % 3 == 0,
655
+ j in range(10)
656
+ when j % 2 == 1
657
+ ) -> ii + j;
658
+
659
+ // Using previous bindings in subsequent iterations
660
+ for (x in [1, 2], y in [x, 2 * x]) -> x * y;
661
+ // => [1, 2, 4, 8]
662
+
299
663
  // Object iteration
300
664
  for (entry in { a: 1, b: 2 } let [key, value] = entry) -> key ++ ":" ++ str(value);
301
665
  // => ["a:1", "b:2"]
@@ -319,8 +683,41 @@ loop (n = 5, acc = 1) -> {
319
683
  else
320
684
  recur(n - 1, acc * n)
321
685
  end
322
- }
686
+ };
323
687
  // => 120 (factorial of 5)
688
+
689
+ // Complex loop with multiple variables
690
+ loop (items = [1, 2, 3, 4, 5], sum = 0, cnt = 0) -> {
691
+ if empty?(items) then
692
+ { sum: sum, average: sum / cnt }
693
+ else
694
+ recur(rest(items), sum + first(items), cnt + 1)
695
+ end
696
+ };
697
+ ```
698
+
699
+ ### Recursion with Recur
700
+
701
+ #### Function Recursion
702
+
703
+ ```lits
704
+ // Simple recursive function with recur
705
+ let factorial = (n) -> {
706
+ if n <= 1 then
707
+ 1
708
+ else
709
+ n * recur(n - 1)
710
+ end
711
+ };
712
+
713
+ // Tail-recursive function
714
+ let sumToN = (n, acc = 0) -> {
715
+ if zero?(n) then
716
+ acc
717
+ else
718
+ recur(n - 1, acc + n)
719
+ end
720
+ };
324
721
  ```
325
722
 
326
723
  ### Error Handling
@@ -329,6 +726,7 @@ loop (n = 5, acc = 1) -> {
329
726
 
330
727
  ```lits
331
728
  // Basic try/catch
729
+ let riskyOperation = () -> throw("Something went wrong");
332
730
  try
333
731
  riskyOperation()
334
732
  catch
@@ -341,6 +739,16 @@ try
341
739
  catch (error)
342
740
  "Error: " ++ error.message
343
741
  end;
742
+
743
+ // Try-catch for graceful degradation
744
+ let parseData = () -> { value: 42 };
745
+ let process = (val) -> val * 2;
746
+ try
747
+ let { value } = parseData();
748
+ process(value)
749
+ catch
750
+ "Using default value"
751
+ end;
344
752
  ```
345
753
 
346
754
  #### Throw
@@ -348,18 +756,147 @@ end;
348
756
  ```lits
349
757
  // Throwing errors
350
758
  try
351
- throw("Custom error message");
759
+ throw("Custom error message")
352
760
  catch
353
761
  "Caught an error"
354
762
  end;
355
763
 
356
- // In context
764
+ // Custom error messages in functions
357
765
  let divide = (a, b) ->
358
766
  if zero?(b) then
359
- throw("Division by zero")
767
+ throw("Cannot divide by zero")
360
768
  else
361
769
  a / b
362
770
  end;
771
+
772
+ // Conditional error throwing
773
+ let validateAge = (age) ->
774
+ cond
775
+ case age < 0 then throw("Age cannot be negative")
776
+ case age > 150 then throw("Age seems unrealistic")
777
+ case true then age
778
+ end;
779
+ ```
780
+
781
+ ### Block Expressions
782
+
783
+ ```lits
784
+ // Block for grouping expressions
785
+ let computeX = () -> 5;
786
+ let computeY = () -> 10;
787
+ let processResult = (z) -> z * 2;
788
+
789
+ let result = {
790
+ let x = computeX();
791
+ let y = computeY();
792
+ let z = x * y;
793
+ processResult(z)
794
+ };
795
+
796
+ // Block with side effects
797
+ let loadData = () -> [1, 2, 3];
798
+ let processData = (data) -> data map -> $ * 2;
799
+
800
+ {
801
+ write!("Starting process...");
802
+ let data = loadData();
803
+ let processed = processData(data);
804
+ write!("Process completed");
805
+ processed
806
+ }
807
+ ```
808
+
809
+ ### Array and Object Construction
810
+
811
+ #### Array Construction
812
+
813
+ ```lits
814
+ // Array literal
815
+ [1, 2, 3, 4];
816
+
817
+ // Array function
818
+ array(1, 2, 3, 4);
819
+
820
+ // With spread
821
+ let small-set = [3, 4, 5];
822
+ [1, 2, ...small-set, 6];
823
+ // => [1, 2, 3, 4, 5, 6]
824
+ ```
825
+
826
+ #### Array Spread
827
+
828
+ ```lits
829
+ // Spread in array literals
830
+ let combined = [1, 2, ...[3, 4, 5], 6];
831
+ // => [1, 2, 3, 4, 5, 6]
832
+
833
+ // Multiple spreads
834
+ let start = [1, 2];
835
+ let middle = [3, 4];
836
+ let stop = [5, 6];
837
+ let result = [...start, ...middle, ...stop];
838
+ ```
839
+
840
+ #### Object Construction
841
+
842
+ ```lits
843
+ // Object literal with static keys
844
+ { name: "John", age: 30 };
845
+
846
+ // Object literal with dynamic keys using bracket notation
847
+ let keyName = "dynamic";
848
+ { [keyName]: "value", ["computed" ++ "Key"]: 42 };
849
+ // => { dynamic: "value", computedKey: 42 }
850
+
851
+ // Object function
852
+ object("name", "John", "age", 30);
853
+
854
+ // With spread
855
+ let defaults = { type: "Person", active: true };
856
+ {
857
+ ...defaults,
858
+ name: "John",
859
+ age: 30
860
+ };
861
+ // => { type: "Person", active: true, name: "John", age: 30 }
862
+
863
+ // Combining static and dynamic keys
864
+ let propName = "score";
865
+ {
866
+ id: 123,
867
+ [propName]: 95,
868
+ ["level" ++ "Number"]: 5
869
+ };
870
+ // => { id: 123, score: 95, levelNumber: 5 }
871
+ ```
872
+
873
+ #### Object Spread
874
+
875
+ ```lits
876
+ // Object spread for merging
877
+ let person = {
878
+ name: "John",
879
+ age: 30
880
+ };
881
+
882
+ let employee = {
883
+ ...person,
884
+ id: "E123",
885
+ department: "Engineering"
886
+ };
887
+ // => { name: "John", age: 30, id: "E123", department: "Engineering" }
888
+
889
+ // Spread with override
890
+ let defaults = {
891
+ name: "Default Name",
892
+ theme: "light",
893
+ active: true
894
+ };
895
+
896
+ let updated = {
897
+ ...defaults,
898
+ name: "Custom Name" // Override defaults.name
899
+ };
363
900
  ```
364
901
 
365
902
  ### Logical Operators
@@ -385,77 +922,134 @@ true || "never reached"; // => true
385
922
  ```lits
386
923
  // Null coalescing operator
387
924
  null ?? "default"; // => "default"
388
- undefined ?? "default"; // => "default"
389
925
  0 ?? "default"; // => 0 (only null/undefined are coalesced)
390
926
  false ?? "default"; // => false
391
927
  "" ?? "default"; // => ""
392
928
  ```
393
929
 
394
- ### Blocks
930
+ ### Ternary Operator
395
931
 
396
932
  ```lits
397
- // Block expressions
398
- {
399
- let a = 1 + 2 + 3;
400
- let b = x -> x * x;
401
- b(a)
402
- }
403
- // => 36 (returns value of last expression)
933
+ // Conditional expression
934
+ let age = 25;
935
+ let result = age >= 18 ? "adult" : "minor";
936
+
937
+ // Nested ternary
938
+ let score = 85;
939
+ let category = score >= 90 ? "A" : score >= 80 ? "B" : "C";
940
+
941
+ // With complex expressions
942
+ let isLoggedIn = () -> true;
943
+ let hasPermission = () -> true;
944
+ let status = isLoggedIn() && hasPermission() ? "authorized" : "unauthorized";
404
945
  ```
405
946
 
406
- ### Arrays and Objects
947
+ ## Variable Names
407
948
 
408
- #### Array Construction
949
+ Lits is generous with variable naming conventions, allowing a wide range of characters that would be invalid in many other programming languages.
409
950
 
410
- ```lits
411
- // Array literal
412
- [1, 2, 3, 4];
951
+ ### Basic Rules
413
952
 
414
- // Array function
415
- array(1, 2, 3, 4);
953
+ Variable names in Lits can contain almost any character except for a small set of reserved symbols. The only restrictions are:
416
954
 
417
- // With spread
418
- let small-set = [3, 4, 5];
419
- [1, 2, ...small-set, 6];
420
- // => [1, 2, 3, 4, 5, 6];
421
- ```
955
+ **Illegal characters anywhere in a variable name:**
956
+ - Parentheses: `(` `)`
957
+ - Brackets: `[` `]`
958
+ - Braces: `{` `}`
959
+ - Quotes: `'` `"` `` ` ``
960
+ - Punctuation: `,` `.` `;`
961
+ - Whitespace: spaces, newlines, tabs
422
962
 
423
- #### Object Construction
963
+ **Additional restrictions for the first character:**
964
+ - Cannot start with digits `0-9`
965
+
966
+ ### Unicode and Emoji Support
967
+
968
+ Beyond these minimal restrictions, Lits supports Unicode characters, including emojis, in variable names:
424
969
 
425
970
  ```lits
426
- // Object literal
427
- { name: "John", age: 30 };
971
+ // Unicode characters are welcome
972
+ let résultat = 42;
973
+ let naïve = "simple approach";
974
+ let coöperation = "working together";
975
+
976
+ // Emojis work too!
977
+ let 😅 = "grinning face with sweat";
978
+ let 🚀 = "rocket ship";
979
+ let result = 😅 ++ " " ++ 🚀;
980
+ // => "grinning face with sweat rocket ship"
981
+ ```
428
982
 
429
- // Object function
430
- object("name", "John", "age", 30);
983
+ ### Quoted Variable Names
431
984
 
432
- // With spread
433
- let defaults = { type: "Person", active: true };
434
- {
435
- ...defaults,
436
- name: "John",
437
- age: 30
438
- };
439
- // => { type: "Person", active: true, name: "John", age: 30 }
985
+ For cases where you need to use the normally illegal characters in variable names, Lits supports quoted variable names using single quotes:
986
+
987
+ ```lits
988
+ // Variables with spaces and special characters
989
+ let 'A strange variable' = 42;
990
+ let 'user.name' = "John Doe";
991
+ let 'data[0]' = "first element";
992
+ let 'function()' = "callable";
993
+
994
+ // Access them the same way
995
+ 'A strange variable' + 8;
996
+ // => 50
440
997
  ```
441
998
 
442
- ### Recursion
999
+ ### Practical Examples
443
1000
 
444
- #### Recur
1001
+ Here are some examples showcasing the flexibility of Lits variable naming:
445
1002
 
446
1003
  ```lits
447
- // Tail recursion with recur
448
- let countdown = n -> {
449
- write!(n);
450
- if pos?(n) then
451
- recur(n - 1)
452
- end
453
- };
1004
+ // Mathematical notation with Greek letters (avoiding reserved symbols)
1005
+ let α = 0.5;
1006
+ let β = 1.2;
1007
+ let γ = 2.0;
1008
+ let Δ = β - α;
1009
+
1010
+ // Descriptive names with special characters
1011
+ let user-name = "alice";
1012
+ let is-valid? = true;
1013
+ let counter! = 0;
1014
+
1015
+ // Mixed styles
1016
+ let dataSet₁ = [1, 2, 3];
1017
+ let dataSet₂ = [4, 5, 6];
1018
+ let 🔧config = { debug: true };
1019
+ ```
1020
+
1021
+ ### Important: Operator Spacing
454
1022
 
455
- countdown(3)
456
- // Prints: 3 2 1 0
1023
+ Due to Lits' flexible variable naming, **operators must be separated by whitespace**. This is crucial to understand:
1024
+
1025
+ ```lits
1026
+ // This is a variable name, NOT addition!
1027
+ let x+1 = 42;
1028
+ let result1 = x+1; // => 42
1029
+
1030
+ // To perform addition, use spaces
1031
+ let x = 5;
1032
+ let result2 = x + 1; // => 6
1033
+
1034
+ // More examples of what looks like operations but are actually variable names
1035
+ let a-b = "subtraction variable";
1036
+ let c*d = "multiplication variable";
1037
+ let e/f = "division variable";
1038
+ let g<h = "comparison variable";
1039
+
1040
+ // To use these as actual operations, add spaces
1041
+ let a = 10;
1042
+ let b = 3;
1043
+ let a-sum = a + b; // Addition
1044
+ let a-diff = a - b; // Subtraction
1045
+ let a-prod = a * b; // Multiplication
1046
+ let a-quot = a / b; // Division
1047
+ let a-comp = a < b; // Comparison
457
1048
  ```
458
1049
 
1050
+ Without whitespace, Lits treats the entire sequence as a single variable identifier. This applies to all operators, including comparison operators, logical operators, and arithmetic operators.
1051
+
1052
+ This flexibility allows for expressive and readable code while maintaining the functional programming paradigm that Lits embodies.
459
1053
  ## Operators and Functions
460
1054
 
461
1055
  ### Algebraic Notation
@@ -495,6 +1089,123 @@ let divide = /(10, _);
495
1089
  divide(2); // => 5
496
1090
  ```
497
1091
 
1092
+ ### Data Types as Functions
1093
+
1094
+ Lits allows arrays, objects, numbers, and strings to be used as functions. This creates elegant, flexible code where data structures become accessors.
1095
+
1096
+ #### Arrays and Numbers as Index Accessors
1097
+
1098
+ Arrays can be called with an index to get an element, and numbers can be called with collections to access that index:
1099
+
1100
+ ```lits
1101
+ let arr = [10, 20, 30, 40];
1102
+
1103
+ // Array as function (accessing by index)
1104
+ arr(0); // => 10
1105
+ arr(2); // => 30
1106
+
1107
+ // Number as function (accessing array at that index)
1108
+ 2(arr); // => 30 (same as arr(2))
1109
+ 0(arr); // => 10 (same as arr(0))
1110
+ ```
1111
+
1112
+ #### Strings and Numbers for Character Access
1113
+
1114
+ Similar to arrays, strings support indexed access in both directions:
1115
+
1116
+ ```lits
1117
+ let name = "Albert";
1118
+
1119
+ // String as function (accessing character by index)
1120
+ name(0); // => "A"
1121
+ name(2); // => "b"
1122
+
1123
+ // Number as function (accessing string at that index)
1124
+ 2(name); // => "b" (same as name(2))
1125
+ 4(name); // => "r" (same as name(4))
1126
+ ```
1127
+
1128
+ #### Objects and Strings as Property Accessors
1129
+
1130
+ Objects can be called with property names, and strings can be called with objects to access properties:
1131
+
1132
+ ```lits
1133
+ let person = { foo: 1, bar: 2, name: "John" };
1134
+
1135
+ // Object as function (accessing property by key)
1136
+ person("foo"); // => 1
1137
+ person("name"); // => "John"
1138
+
1139
+ // String as function (accessing object property)
1140
+ "foo"(person); // => 1 (same as person("foo"))
1141
+ "bar"(person); // => 2 (same as person("bar"))
1142
+ ```
1143
+
1144
+ #### Powerful Higher-Order Function Applications
1145
+
1146
+ This feature makes higher-order functions incredibly flexible. You can pass data directly as accessor functions:
1147
+
1148
+ ```lits
1149
+ let data = [
1150
+ { name: "Alice", score: 95 },
1151
+ { name: "Bob", score: 87 },
1152
+ { name: "Carol", score: 92 }
1153
+ ];
1154
+
1155
+ // Extract names using string as function
1156
+ data map "name";
1157
+ // => ["Alice", "Bob", "Carol"]
1158
+
1159
+ // Extract scores using string as function
1160
+ data map "score";
1161
+ // => [95, 87, 92]
1162
+
1163
+ // Get second element of multiple arrays using number as function
1164
+ let arrays = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
1165
+ arrays map 1;
1166
+ // => [2, 5, 8]
1167
+
1168
+ // Access nested data
1169
+ let records = [
1170
+ { values: [10, 20, 30] },
1171
+ { values: [40, 50, 60] },
1172
+ { values: [70, 80, 90] }
1173
+ ];
1174
+
1175
+ // Get first value from each record's values array
1176
+ records map "values" map 0;
1177
+ // => [10, 40, 70]
1178
+ ```
1179
+
1180
+ #### Practical Examples
1181
+
1182
+ ```lits
1183
+ // Matrix column extraction
1184
+ let matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
1185
+ matrix map 1; // => [2, 5, 8] (second column)
1186
+
1187
+ // Object property extraction
1188
+ let users = [
1189
+ { id: 1, active: true },
1190
+ { id: 2, active: false },
1191
+ { id: 3, active: true }
1192
+ ];
1193
+ users map "active"; // => [true, false, true]
1194
+
1195
+ // String character extraction
1196
+ let words = ["hello", "world", "test"];
1197
+ words map 0; // => ["h", "w", "t"] (first characters)
1198
+
1199
+ // Complex data navigation
1200
+ let sales = [
1201
+ { quarter: "Q1", regions: { north: 100, south: 200 } },
1202
+ { quarter: "Q2", regions: { north: 150, south: 180 } }
1203
+ ];
1204
+ sales map "regions" map "north"; // => [100, 150]
1205
+ ```
1206
+
1207
+ This feature eliminates the need for verbose accessor functions and makes data transformation pipelines more concise and readable.
1208
+
498
1209
  ### Parameter Order
499
1210
 
500
1211
  Lits favors subject-first parameter order for better operator chaining:
@@ -513,7 +1224,7 @@ The pipe operator `|>` passes the result of the left expression as the first arg
513
1224
 
514
1225
  ```lits
515
1226
  // Without pipe operator
516
- reduce(map(filter([1, 2, 3, 4, 5, 6], odd?), -> $ + $), +, 0);
1227
+ reduce(map(filter([1, 2, 3, 4, 5, 6], odd?), -> $ * $), +, 0);
517
1228
 
518
1229
  // With pipe operator (much more readable)
519
1230
  [1, 2, 3, 4, 5, 6]
@@ -546,6 +1257,85 @@ reduce(map(filter([1, 2, 3, 4, 5, 6], odd?), -> $ + $), +, 0);
546
1257
  // => 18 (even numbers [2, 4] -> [6, 12] -> sum = 18)
547
1258
  ```
548
1259
 
1260
+ ### Operator Precedence
1261
+
1262
+ Lits follows a specific operator precedence order that determines how expressions are evaluated. Operators with higher precedence are evaluated first. When operators have the same precedence, they are evaluated left-to-right.
1263
+
1264
+ Here's the complete precedence table, from highest to lowest:
1265
+
1266
+ | Precedence | Operator(s) | Description | Example |
1267
+ |------------|-------------|-------------|---------|
1268
+ | 12 | `^` | Exponentiation | `2 ^ 3 ^ 2` → `2 ^ (3 ^ 2)` → `512` |
1269
+ | 11 | `*` `/` `%` | Multiplication, Division, Remainder | `6 + 4 * 2` → `6 + 8` → `14` |
1270
+ | 10 | `+` `-` | Addition, Subtraction | `10 - 3 + 2` → `7 + 2` → `9` |
1271
+ | 9 | `<<` `>>` `>>>` | Bit shift operations | `8 >> 1 + 1` → `8 >> 2` → `2` |
1272
+ | 8 | `++` | String concatenation | `"a" ++ "b" ++ "c"` → `"abc"` |
1273
+ | 7 | `<` `<=` `≤` `>` `>=` `≥` | Comparison operators | `3 + 2 > 4` → `5 > 4` → `true` |
1274
+ | 6 | `==` `!=` `≠` | Equality operators | `2 * 3 == 6` → `6 == 6` → `true` |
1275
+ | 5 | `&` `xor` `\|` | Bitwise operations | `4 \| 2 & 1` → `4 \| 0` → `4` |
1276
+ | 4 | `&&` `\|\|` `??` | Logical operations | `true && false \|\| true` → `false \|\| true` → `true` |
1277
+ | 3 | *function operators* | Binary functions used as operators | `5 max 3 + 2` → `5 max 5` → `5` |
1278
+ | 2 | `\|>` | Pipe operator | `[1,2] \|> map(_, inc) \|> sum` |
1279
+ | 1 | `?` `:` | Conditional (ternary) operator | `true ? 1 + 2 : 3` → `true ? 3 : 3` → `3` |
1280
+
1281
+ #### Examples of Precedence in Action
1282
+
1283
+ ```lits
1284
+ // Exponentiation has highest precedence
1285
+ 2 + 3 ^ 2; // => 2 + 9 = 11 (not 5^2 = 25)
1286
+
1287
+ // Multiplication before addition
1288
+ 2 + 3 * 4; // => 2 + 12 = 14 (not 5*4 = 20)
1289
+
1290
+ // String concatenation before comparison
1291
+ "a" ++ "b" == "ab"; // => "ab" == "ab" = true
1292
+
1293
+ // Comparison before logical AND
1294
+ 3 > 2 && 1 < 2; // => true && true = true
1295
+
1296
+ // Pipe has very low precedence
1297
+ [1, 2, 3] |> map(_, inc) |> vec:sum; // Evaluates left to right
1298
+
1299
+ // Conditional has lowest precedence
1300
+ true ? 2 + 3 : 4 + 5; // => true ? 5 : 9 = 5
1301
+ ```
1302
+
1303
+ #### Using Parentheses
1304
+
1305
+ When in doubt, or to make your intent clear, use parentheses to override precedence:
1306
+
1307
+ ```lits
1308
+ // Without parentheses (follows precedence)
1309
+ 2 + 3 * 4; // => 14
1310
+
1311
+ // With parentheses (explicit grouping)
1312
+ (2 + 3) * 4; // => 20
1313
+
1314
+ // Complex expression with explicit grouping
1315
+ let a = 2;
1316
+ let b = 3;
1317
+ let c = 4;
1318
+ let d = true;
1319
+ let e = false;
1320
+ let f = 10;
1321
+ let g = 5;
1322
+ ((a + b) * c) > (d && e ? f : g) // => (5 * 4) > (false ? 10 : 5) = 20 > 5 = true;
1323
+ ```
1324
+
1325
+ #### Associativity
1326
+
1327
+ Most operators are left-associative, meaning they evaluate from left to right when they have the same precedence:
1328
+
1329
+ ```lits
1330
+ 10 - 5 - 2; // => (10 - 5) - 2 = 3 (not 10 - (5 - 2) = 7)
1331
+ "a" ++ "b" ++ "c"; // => ("a" ++ "b") ++ "c" = "abc"
1332
+ ```
1333
+
1334
+ **Exception**: Exponentiation (`^`) is right-associative:
1335
+ ```lits
1336
+ 2 ^ 3 ^ 2 // => 2 ^ (3 ^ 2) = 2 ^ 9 = 512 (not (2 ^ 3) ^ 2 = 64)
1337
+ ```
1338
+
549
1339
  ## Built-in Functions
550
1340
 
551
1341
  Lits comes with a comprehensive standard library of functions for:
@@ -562,13 +1352,34 @@ Lits comes with a comprehensive standard library of functions for:
562
1352
 
563
1353
  For a complete reference of all available functions with examples, visit the [Lits Playground](https://mojir.github.io/lits/) where you can explore the interactive documentation and try functions in real-time.
564
1354
 
1355
+ ## Serialization
1356
+
1357
+ A unique feature of Lits is that every result from evaluation is fully serializable as JSON, including functions and regular expressions:
1358
+
1359
+ ```lits
1360
+ // Functions are serializable
1361
+ let myFunction = x -> x * 2;
1362
+
1363
+ // Regular expressions are serializable
1364
+ let myRegex = #"[a-z]+";
1365
+
1366
+ // Complex data structures with functions are serializable
1367
+ let config = {
1368
+ transform: x -> x * 3,
1369
+ pattern: #"\d+",
1370
+ data: [1, 2, 3]
1371
+ };
1372
+
1373
+ // All of these can be serialized to JSON and later deserialized
1374
+ // back into working Lits values, preserving their functionality
1375
+ ```
1376
+
565
1377
  ## Modules and Exports
566
1378
 
567
1379
  ```lits
568
1380
  // Export variables and functions
569
1381
  export let pi = 3.14159;
570
1382
  export let square = x -> x * x;
571
-
572
1383
  // Exported values become available to other modules
573
1384
  ```
574
1385
 
@@ -577,14 +1388,9 @@ export let square = x -> x * x;
577
1388
  ### Factorial
578
1389
 
579
1390
  ```lits
580
- let factorial = n ->
581
- if n <= 1 then
582
- 1
583
- else
584
- n * self(n - 1)
585
- end;
1391
+ let factorial = n -> n <= 1 ? 1 : n * self(n - 1);
586
1392
 
587
- factorial(5) // => 120
1393
+ factorial(5); // => 120
588
1394
  ```
589
1395
 
590
1396
  ### Array Processing
@@ -605,99 +1411,67 @@ let oddSum = numbers
605
1411
  // => 25
606
1412
  ```
607
1413
 
608
- ### Object Manipulation
1414
+ ### String Processing
609
1415
 
610
1416
  ```lits
611
- let person = { name: "John", age: 30, city: "New York" };
612
-
613
- // Update age
614
- let older = assoc(person, "age", 31);
615
-
616
- // Add new field
617
- let withJob = assoc(older, "job", "Developer");
618
-
619
- // Using pipe operator for chaining
620
- let updated = person
621
- |> assoc(_, "age", 31)
622
- |> assoc(_, "job", "Developer");
1417
+ let text = "Hello, World! How are you today?";
1418
+
1419
+ // Word count
1420
+ let wordCount = text
1421
+ |> split(_, #"\s+")
1422
+ |> count;
1423
+ // => 6
1424
+
1425
+ // Uppercase words longer than 4 characters
1426
+ let longWords = text
1427
+ |> split(_, #"\s+")
1428
+ |> filter(_, -> count($) > 4)
1429
+ |> map(_, upper-case);
1430
+ // => ["HELLO,", "WORLD!", "TODAY?"]
623
1431
  ```
624
1432
 
625
- ### Pattern Matching Examples
626
-
627
- #### Switch (for matching against a single value)
1433
+ ### Data Transformation
628
1434
 
629
1435
  ```lits
630
- let gradeToLetter = grade ->
631
- switch grade
632
- case 90 then "A"
633
- case 80 then "B"
634
- case 70 then "C"
635
- case 60 then "D"
636
- end ?? "F";
637
-
638
- gradeToLetter(80); // => "B"
639
- gradeToLetter(55); // => "F"
640
-
641
- // HTTP status code handling
642
- let statusMessage = code ->
643
- switch code
644
- case 200 then "OK"
645
- case 404 then "Not Found"
646
- case 500 then "Internal Server Error"
647
- end ?? "Unknown Status";
648
- ```
649
-
650
- #### Cond (for testing different conditions)
651
-
652
- ```lits
653
- let describe = x ->
654
- cond
655
- case null?(x) then "nothing"
656
- case number?(x) then "a number: " ++ str(x)
657
- case string?(x) then "text: " ++ x
658
- case array?(x) then "list of " ++ str(count(x)) ++ " items"
659
- end ?? "unknown type";
660
-
661
- describe(42); // => "a number: 42"
662
- describe([1,2,3]); // => "list of 3 items"
663
-
664
- // Grade ranges
665
- let gradeToLetter = score ->
666
- cond
667
- case score >= 90 then "A"
668
- case score >= 80 then "B"
669
- case score >= 70 then "C"
670
- case score >= 60 then "D"
671
- end ?? "F";
672
-
673
- gradeToLetter(85); // => "B"
674
- gradeToLetter(55); // => "F"
1436
+ let users = [
1437
+ { name: "Alice", age: 30, department: "Engineering" },
1438
+ { name: "Bob", age: 25, department: "Marketing" },
1439
+ { name: "Charlie", age: 35, department: "Engineering" }
1440
+ ];
1441
+
1442
+ // Group by department and get average age
1443
+ let grouped = users |> group-by(_, "department");
1444
+ let departmentAges = grouped
1445
+ |> entries(_)
1446
+ |> map(_, ([dept, people]) -> {
1447
+ let ages = people |> map(_, "age");
1448
+ let total = ages |> reduce(_, +, 0);
1449
+ [dept, total / count(ages)]
1450
+ })
1451
+ |> (pairs -> zipmap(map(pairs, 0), map(pairs, 1)));
1452
+ // => { "Engineering": 32.5, "Marketing": 25 }
675
1453
  ```
676
1454
 
677
1455
  ## JavaScript Interoperability
678
1456
 
679
- Lits provides a comprehensive JavaScript API for embedding and integration:
1457
+ ### Using Lits in JavaScript
680
1458
 
681
1459
  ```javascript
682
- import { Lits } from 'lits';
1460
+ import { Lits } from '@mojir/lits';
683
1461
 
684
- // Create a Lits instance
685
1462
  const lits = new Lits();
686
1463
 
687
- // Run Lits code
688
- const result = lits.run('+(5, 3)');
689
- console.log(result); // 8
1464
+ // Basic usage
1465
+ const result1 = lits.run('+(1, 2, 3)');
1466
+ console.log(result1); // 6
690
1467
 
691
- // Pass JavaScript values to Lits
1468
+ // Provide JavaScript values
692
1469
  const result2 = lits.run('name ++ " is " ++ str(age)', {
693
- values: {
694
- name: "John",
695
- age: 30
696
- }
1470
+ values: { name: 'John', age: 30 }
697
1471
  });
698
1472
  console.log(result2); // "John is 30"
699
1473
 
700
- // Expose JavaScript functions to Lits
1474
+ // Expose JavaScript functions
701
1475
  const result3 = lits.run('myAlert("Hello from Lits!")', {
702
1476
  jsFunctions: {
703
1477
  myAlert: {