@mojir/lits 2.1.36 → 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 +739 -61
- package/dist/cli/cli/src/modules/index.d.ts +4 -0
- package/dist/cli/cli/src/modules/sys/index.d.ts +3 -0
- package/dist/cli/cli.js +141 -44
- package/dist/cli/src/evaluator/ContextStack.d.ts +2 -2
- package/dist/cli/src/modules/index.d.ts +4 -0
- package/dist/cli/src/modules/sys/index.d.ts +3 -0
- package/dist/cli/src/parser/types.d.ts +3 -0
- package/dist/index.esm.js +62 -21
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +62 -21
- package/dist/index.js.map +1 -1
- package/dist/lits.iife.js +62 -21
- package/dist/lits.iife.js.map +1 -1
- package/dist/src/evaluator/ContextStack.d.ts +2 -2
- package/dist/src/parser/types.d.ts +3 -0
- package/dist/testFramework.esm.js +62 -21
- package/dist/testFramework.esm.js.map +1 -1
- package/dist/testFramework.js +62 -21
- package/dist/testFramework.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Lits
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
99
|
-
|
|
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)
|
|
161
|
+
|
|
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)
|
|
100
257
|
|
|
101
|
-
|
|
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
|
-
//
|
|
105
|
-
|
|
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
|
|
106
314
|
```
|
|
107
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)
|
|
336
|
+
```
|
|
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:
|
|
@@ -192,7 +424,7 @@ let {
|
|
|
192
424
|
settings = { theme: "light" },
|
|
193
425
|
scores as userScores = [],
|
|
194
426
|
...others
|
|
195
|
-
} = { name: "Sam", profile: { contact: {} }};
|
|
427
|
+
} = { name: "Sam", profile: { contact: {} } };
|
|
196
428
|
// userName => "Sam", age => 0, userEmail => "none", etc.
|
|
197
429
|
```
|
|
198
430
|
|
|
@@ -214,11 +446,15 @@ let { name, scores: [one, two] } = { name: "Uma", scores: [85, 92] };
|
|
|
214
446
|
// Array destructuring
|
|
215
447
|
let [, , a, b] = [1, 2, 3, 4];
|
|
216
448
|
// a => 3, b => 4
|
|
449
|
+
```
|
|
217
450
|
|
|
451
|
+
```lits
|
|
218
452
|
// Array destructuring with defaults
|
|
219
453
|
let [one, two = 2] = [1];
|
|
220
454
|
// one => 1, two => 2
|
|
455
|
+
```
|
|
221
456
|
|
|
457
|
+
```lits
|
|
222
458
|
// Skipping elements
|
|
223
459
|
let [x, , z] = [1, 2, 3];
|
|
224
460
|
// x => 1, z => 3
|
|
@@ -230,11 +466,15 @@ let [x, , z] = [1, 2, 3];
|
|
|
230
466
|
// Array rest pattern
|
|
231
467
|
let [head, ...tail] = [1, 2, 3, 4];
|
|
232
468
|
// head => 1, tail => [2, 3, 4]
|
|
469
|
+
```
|
|
233
470
|
|
|
471
|
+
```lits
|
|
234
472
|
// Object rest pattern
|
|
235
473
|
let { name, ...otherProps } = { name: "John", age: 30, city: "NYC" };
|
|
236
474
|
// name => "John", otherProps => { age: 30, city: "NYC" }
|
|
475
|
+
```
|
|
237
476
|
|
|
477
|
+
```lits
|
|
238
478
|
// Empty rest patterns
|
|
239
479
|
let [only, ...empty] = [1];
|
|
240
480
|
// only => 1, empty => []
|
|
@@ -247,18 +487,24 @@ let [only, ...empty] = [1];
|
|
|
247
487
|
let greet = ({ name }) -> "Hello, " ++ name;
|
|
248
488
|
greet({ name: "Pat" });
|
|
249
489
|
// => "Hello, Pat"
|
|
490
|
+
```
|
|
250
491
|
|
|
492
|
+
```lits
|
|
251
493
|
// With defaults in parameters
|
|
252
494
|
let greet2 = ({ name = "friend" }) -> "Hello, " ++ name;
|
|
253
495
|
greet2({});
|
|
254
496
|
// => "Hello, friend"
|
|
497
|
+
```
|
|
255
498
|
|
|
499
|
+
```lits
|
|
256
500
|
// Nested parameter destructuring
|
|
257
501
|
let processUser = ({ profile: { name, age }}) ->
|
|
258
502
|
name ++ " is " ++ str(age);
|
|
259
503
|
processUser({ profile: { name: "Quinn", age: 29 }});
|
|
260
504
|
// => "Quinn is 29"
|
|
505
|
+
```
|
|
261
506
|
|
|
507
|
+
```lits
|
|
262
508
|
// Array parameter destructuring
|
|
263
509
|
let processCoords = ([x, y]) -> x + y;
|
|
264
510
|
processCoords([3, 4]);
|
|
@@ -299,56 +545,59 @@ let factorial = n ->
|
|
|
299
545
|
#### If/Unless
|
|
300
546
|
|
|
301
547
|
```lits
|
|
302
|
-
let x =
|
|
548
|
+
let x = 15; // Fixed value for compilation
|
|
303
549
|
|
|
304
550
|
if x > 10 then
|
|
305
551
|
"large"
|
|
306
552
|
else
|
|
307
553
|
"small"
|
|
308
554
|
end;
|
|
309
|
-
// => "large"
|
|
555
|
+
// => "large"
|
|
310
556
|
|
|
311
557
|
// If without else returns null
|
|
312
558
|
if false then "never" end;
|
|
313
559
|
// => null
|
|
314
560
|
|
|
315
561
|
// Unless (inverted if)
|
|
316
|
-
let y =
|
|
562
|
+
let y = 8;
|
|
317
563
|
unless y > 10 then
|
|
318
564
|
"small"
|
|
319
565
|
else
|
|
320
566
|
"large"
|
|
321
567
|
end;
|
|
322
|
-
// => "small"
|
|
568
|
+
// => "small"
|
|
323
569
|
```
|
|
324
570
|
|
|
325
571
|
#### Cond
|
|
326
572
|
|
|
327
573
|
```lits
|
|
328
|
-
let x =
|
|
574
|
+
let x = 12;
|
|
329
575
|
|
|
330
576
|
// Multi-branch conditional
|
|
331
577
|
cond
|
|
332
578
|
case x < 5 then "small"
|
|
333
579
|
case x < 10 then "medium"
|
|
334
580
|
case x < 15 then "large"
|
|
335
|
-
|
|
336
|
-
|
|
581
|
+
case true then "extra large" // default case
|
|
582
|
+
end;
|
|
583
|
+
// => "large"
|
|
337
584
|
|
|
338
585
|
// Cond with complex conditions
|
|
339
|
-
let urgent =
|
|
340
|
-
let important =
|
|
586
|
+
let urgent = true;
|
|
587
|
+
let important = false;
|
|
341
588
|
let priority = cond
|
|
342
589
|
case urgent && important then "critical"
|
|
343
590
|
case urgent then "high"
|
|
344
591
|
case important then "medium"
|
|
345
|
-
|
|
592
|
+
case true then "low"
|
|
593
|
+
end;
|
|
594
|
+
// => "high"
|
|
346
595
|
```
|
|
347
596
|
|
|
348
597
|
#### Switch
|
|
349
598
|
|
|
350
599
|
```lits
|
|
351
|
-
let x =
|
|
600
|
+
let x = 1;
|
|
352
601
|
|
|
353
602
|
// Switch on value
|
|
354
603
|
switch x
|
|
@@ -356,19 +605,20 @@ switch x
|
|
|
356
605
|
case 1 then "one"
|
|
357
606
|
case 2 then "two"
|
|
358
607
|
end;
|
|
359
|
-
// => "
|
|
608
|
+
// => "one"
|
|
360
609
|
|
|
361
610
|
// Switch with multiple cases
|
|
362
611
|
let userInput = "help";
|
|
363
|
-
let exit = -> "exiting";
|
|
364
|
-
let showHelp = -> "showing help";
|
|
365
|
-
let saveData = -> "saving data";
|
|
612
|
+
let exit = () -> "exiting";
|
|
613
|
+
let showHelp = () -> "showing help";
|
|
614
|
+
let saveData = () -> "saving data";
|
|
366
615
|
|
|
367
616
|
switch userInput
|
|
368
617
|
case "quit" then exit()
|
|
369
618
|
case "help" then showHelp()
|
|
370
619
|
case "save" then saveData()
|
|
371
620
|
end;
|
|
621
|
+
// => "showing help"
|
|
372
622
|
```
|
|
373
623
|
|
|
374
624
|
### Loops and Iteration
|
|
@@ -495,7 +745,7 @@ let parseData = () -> { value: 42 };
|
|
|
495
745
|
let process = (val) -> val * 2;
|
|
496
746
|
try
|
|
497
747
|
let { value } = parseData();
|
|
498
|
-
process(value)
|
|
748
|
+
process(value)
|
|
499
749
|
catch
|
|
500
750
|
"Using default value"
|
|
501
751
|
end;
|
|
@@ -553,10 +803,25 @@ let processData = (data) -> data map -> $ * 2;
|
|
|
553
803
|
let processed = processData(data);
|
|
554
804
|
write!("Process completed");
|
|
555
805
|
processed
|
|
556
|
-
}
|
|
806
|
+
}
|
|
557
807
|
```
|
|
558
808
|
|
|
559
|
-
### Array and Object
|
|
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
|
+
```
|
|
560
825
|
|
|
561
826
|
#### Array Spread
|
|
562
827
|
|
|
@@ -572,6 +837,39 @@ let stop = [5, 6];
|
|
|
572
837
|
let result = [...start, ...middle, ...stop];
|
|
573
838
|
```
|
|
574
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
|
+
|
|
575
873
|
#### Object Spread
|
|
576
874
|
|
|
577
875
|
```lits
|
|
@@ -623,22 +921,21 @@ true || "never reached"; // => true
|
|
|
623
921
|
|
|
624
922
|
```lits
|
|
625
923
|
// Null coalescing operator
|
|
626
|
-
null ?? "default";
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
"" ?? "default"; // => ""
|
|
924
|
+
null ?? "default"; // => "default"
|
|
925
|
+
0 ?? "default"; // => 0 (only null/undefined are coalesced)
|
|
926
|
+
false ?? "default"; // => false
|
|
927
|
+
"" ?? "default"; // => ""
|
|
631
928
|
```
|
|
632
929
|
|
|
633
930
|
### Ternary Operator
|
|
634
931
|
|
|
635
932
|
```lits
|
|
636
933
|
// Conditional expression
|
|
637
|
-
let age =
|
|
934
|
+
let age = 25;
|
|
638
935
|
let result = age >= 18 ? "adult" : "minor";
|
|
639
936
|
|
|
640
937
|
// Nested ternary
|
|
641
|
-
let score =
|
|
938
|
+
let score = 85;
|
|
642
939
|
let category = score >= 90 ? "A" : score >= 80 ? "B" : "C";
|
|
643
940
|
|
|
644
941
|
// With complex expressions
|
|
@@ -647,36 +944,396 @@ let hasPermission = () -> true;
|
|
|
647
944
|
let status = isLoggedIn() && hasPermission() ? "authorized" : "unauthorized";
|
|
648
945
|
```
|
|
649
946
|
|
|
650
|
-
##
|
|
947
|
+
## Variable Names
|
|
948
|
+
|
|
949
|
+
Lits is generous with variable naming conventions, allowing a wide range of characters that would be invalid in many other programming languages.
|
|
950
|
+
|
|
951
|
+
### Basic Rules
|
|
952
|
+
|
|
953
|
+
Variable names in Lits can contain almost any character except for a small set of reserved symbols. The only restrictions are:
|
|
954
|
+
|
|
955
|
+
**Illegal characters anywhere in a variable name:**
|
|
956
|
+
- Parentheses: `(` `)`
|
|
957
|
+
- Brackets: `[` `]`
|
|
958
|
+
- Braces: `{` `}`
|
|
959
|
+
- Quotes: `'` `"` `` ` ``
|
|
960
|
+
- Punctuation: `,` `.` `;`
|
|
961
|
+
- Whitespace: spaces, newlines, tabs
|
|
962
|
+
|
|
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:
|
|
969
|
+
|
|
970
|
+
```lits
|
|
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
|
+
```
|
|
982
|
+
|
|
983
|
+
### Quoted Variable Names
|
|
984
|
+
|
|
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
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
### Practical Examples
|
|
1000
|
+
|
|
1001
|
+
Here are some examples showcasing the flexibility of Lits variable naming:
|
|
1002
|
+
|
|
1003
|
+
```lits
|
|
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
|
|
651
1022
|
|
|
652
|
-
|
|
1023
|
+
Due to Lits' flexible variable naming, **operators must be separated by whitespace**. This is crucial to understand:
|
|
653
1024
|
|
|
654
|
-
|
|
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
|
|
1048
|
+
```
|
|
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.
|
|
1053
|
+
## Operators and Functions
|
|
1054
|
+
|
|
1055
|
+
### Algebraic Notation
|
|
1056
|
+
|
|
1057
|
+
All functions that take two parameters can be used as operators:
|
|
1058
|
+
|
|
1059
|
+
```lits
|
|
1060
|
+
// As a function
|
|
1061
|
+
max(5, 10); // => 10
|
|
1062
|
+
|
|
1063
|
+
// As an operator
|
|
1064
|
+
5 max 10; // => 10
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
All operators can be used as functions:
|
|
1068
|
+
|
|
1069
|
+
```lits
|
|
1070
|
+
// As an operator
|
|
1071
|
+
5 + 3; // => 8
|
|
1072
|
+
|
|
1073
|
+
// As a function
|
|
1074
|
+
+(5, 3); // => 8
|
|
1075
|
+
|
|
1076
|
+
// Partial application with underscore placeholder
|
|
1077
|
+
let add5 = +(5, _);
|
|
1078
|
+
add5(3); // => 8
|
|
1079
|
+
|
|
1080
|
+
// Multiple placeholders
|
|
1081
|
+
let subtractTwoValues = -(100, _, _);
|
|
1082
|
+
subtractTwoValues(4, 3); // => 93
|
|
1083
|
+
|
|
1084
|
+
// Single placeholder in different positions
|
|
1085
|
+
let subtract = -(_, 2);
|
|
1086
|
+
subtract(10); // => 8
|
|
1087
|
+
|
|
1088
|
+
let divide = /(10, _);
|
|
1089
|
+
divide(2); // => 5
|
|
1090
|
+
```
|
|
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
|
+
```
|
|
655
1111
|
|
|
656
|
-
|
|
657
|
-
2. **Array/Object access** - `arr[index]`, `obj.property`
|
|
658
|
-
3. **Unary operators** - `not`, `!`, `-` (negation)
|
|
659
|
-
4. **Exponentiation** - `^` (right-associative)
|
|
660
|
-
5. **Multiplication, Division, Modulo** - `*`, `/`, `%`
|
|
661
|
-
6. **Addition, Subtraction** - `+`, `-`
|
|
662
|
-
7. **String concatenation** - `++`
|
|
663
|
-
8. **Comparison operators** - `<`, `>`, `<=`, `>=`
|
|
664
|
-
9. **Equality operators** - `==`, `!=`, `identical?`
|
|
665
|
-
10. **Logical AND** - `&&`
|
|
666
|
-
11. **Logical OR** - `||`
|
|
667
|
-
12. **Null coalescing** - `??`
|
|
668
|
-
13. **Ternary conditional** - `condition ? true-value : false-value`
|
|
1112
|
+
#### Strings and Numbers for Character Access
|
|
669
1113
|
|
|
670
|
-
|
|
1114
|
+
Similar to arrays, strings support indexed access in both directions:
|
|
671
1115
|
|
|
672
1116
|
```lits
|
|
673
|
-
|
|
674
|
-
5 > 3 && 2 < 4; // => true
|
|
675
|
-
5 > 3 || 2 > 4; // => true
|
|
1117
|
+
let name = "Albert";
|
|
676
1118
|
|
|
677
|
-
//
|
|
678
|
-
|
|
679
|
-
|
|
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
|
+
|
|
1209
|
+
### Parameter Order
|
|
1210
|
+
|
|
1211
|
+
Lits favors subject-first parameter order for better operator chaining:
|
|
1212
|
+
|
|
1213
|
+
```lits
|
|
1214
|
+
// Function style
|
|
1215
|
+
filter([1, 2, 3, 4], odd?); // => [1, 3]
|
|
1216
|
+
|
|
1217
|
+
// Operator style (more readable)
|
|
1218
|
+
[1, 2, 3, 4] filter odd?; // => [1, 3]
|
|
1219
|
+
```
|
|
1220
|
+
|
|
1221
|
+
### Pipe Operator
|
|
1222
|
+
|
|
1223
|
+
The pipe operator `|>` passes the result of the left expression as the first argument to the right function:
|
|
1224
|
+
|
|
1225
|
+
```lits
|
|
1226
|
+
// Without pipe operator
|
|
1227
|
+
reduce(map(filter([1, 2, 3, 4, 5, 6], odd?), -> $ * $), +, 0);
|
|
1228
|
+
|
|
1229
|
+
// With pipe operator (much more readable)
|
|
1230
|
+
[1, 2, 3, 4, 5, 6]
|
|
1231
|
+
|> filter(_, odd?)
|
|
1232
|
+
|> map(_, -> $ * $)
|
|
1233
|
+
|> reduce(_, +, 0);
|
|
1234
|
+
// => 35
|
|
1235
|
+
|
|
1236
|
+
// Simple transformations
|
|
1237
|
+
"hello world"
|
|
1238
|
+
|> upper-case
|
|
1239
|
+
|> split(_, " ")
|
|
1240
|
+
|> reverse
|
|
1241
|
+
|> join(_, "-");
|
|
1242
|
+
// => "WORLD-HELLO"
|
|
1243
|
+
|
|
1244
|
+
// Mathematical operations
|
|
1245
|
+
10
|
|
1246
|
+
|> +(_, 5)
|
|
1247
|
+
|> *(_, 2)
|
|
1248
|
+
|> /(_, 3);
|
|
1249
|
+
// => 10 (10 + 5 = 15, 15 * 2 = 30, 30 / 3 = 10)
|
|
1250
|
+
|
|
1251
|
+
// Data processing pipeline
|
|
1252
|
+
{ numbers: [1, 2, 3, 4, 5], multiplier: 3 }
|
|
1253
|
+
|> get(_, "numbers")
|
|
1254
|
+
|> filter(_, even?)
|
|
1255
|
+
|> map(_, *(_, 3))
|
|
1256
|
+
|> reduce(_, +, 0);
|
|
1257
|
+
// => 18 (even numbers [2, 4] -> [6, 12] -> sum = 18)
|
|
1258
|
+
```
|
|
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)
|
|
680
1337
|
```
|
|
681
1338
|
|
|
682
1339
|
## Built-in Functions
|
|
@@ -695,13 +1352,34 @@ Lits comes with a comprehensive standard library of functions for:
|
|
|
695
1352
|
|
|
696
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.
|
|
697
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
|
+
|
|
698
1377
|
## Modules and Exports
|
|
699
1378
|
|
|
700
1379
|
```lits
|
|
701
1380
|
// Export variables and functions
|
|
702
1381
|
export let pi = 3.14159;
|
|
703
1382
|
export let square = x -> x * x;
|
|
704
|
-
|
|
705
1383
|
// Exported values become available to other modules
|
|
706
1384
|
```
|
|
707
1385
|
|
|
@@ -712,7 +1390,7 @@ export let square = x -> x * x;
|
|
|
712
1390
|
```lits
|
|
713
1391
|
let factorial = n -> n <= 1 ? 1 : n * self(n - 1);
|
|
714
1392
|
|
|
715
|
-
factorial(5) // => 120
|
|
1393
|
+
factorial(5); // => 120
|
|
716
1394
|
```
|
|
717
1395
|
|
|
718
1396
|
### Array Processing
|
|
@@ -741,7 +1419,7 @@ let text = "Hello, World! How are you today?";
|
|
|
741
1419
|
// Word count
|
|
742
1420
|
let wordCount = text
|
|
743
1421
|
|> split(_, #"\s+")
|
|
744
|
-
|> count
|
|
1422
|
+
|> count;
|
|
745
1423
|
// => 6
|
|
746
1424
|
|
|
747
1425
|
// Uppercase words longer than 4 characters
|