Last active
September 1, 2025 16:41
-
-
Save EGreg/8f1e2310e89145b142899ba46d0cf2f7 to your computer and use it in GitHub Desktop.
U Language By Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| U Programming Language By Example | |
| Priorities: | |
| * Simpler than C | |
| * Safer than Rust | |
| * More Obvious than Python | |
| 1. Comments | |
| 1.1 Single-line Comments | |
| // like this | |
| 1.2 Block comments can be nested: | |
| /* /* foo */ bar */ | |
| 2. Keywords | |
| 2.1 All keywords are one letter. | |
| a - async, await, actor | |
| b - break, optionally return a value to loop | |
| c - continue | |
| d - define class | |
| e - error, exception | |
| f - function | |
| r - return | |
| t - this | |
| w - while generator | |
| y - yield | |
| 2.2 Built-in messages are one letter | |
| .l() - length | |
| .c() - copy on write (may copy later) | |
| .n() - new | |
| .x() - execute | |
| 2.3 Types are one letter | |
| B - Bytes / Buffer / Blob (B16, B32 etc.) | |
| D - Double / Decimal | |
| F - Function | |
| I - Integer (I32, I64, etc.) | |
| L - Logical | |
| N - None / Null / Nil | |
| S - String | |
| 2.4 Compound Types (not on stack) | |
| [I] - Array of Integers | |
| [[I]] - Array of Array of Integers | |
| {I} - Dictionary of Strings to Integers | |
| 2.5 Operators | |
| := Assignment Operator (: to prevent accidental mistakes, cannot be chained) | |
| == Equality Test | |
| === Object Equality Test, can be used to test for === N | |
| : The instanceof operator, can be used to test against classes and interfaces (see Classes) | |
| != Not Equal (also Logical XOR) | |
| ! & | Logical Operators (Note: work on Logical) | |
| ~! ~& ~| ~^ Bitwise Operators (Because they're far more rare) | |
| + - * / % ^ Arithmetic Operators (^ is exp) | |
| += -= *= ^= /= %= ^= Arithmetic Assignment, cannot be chained | |
| ~&= ~|= ~^= Bitwise Assignment cannot be chained | |
| ++ Increment (prefix only) | |
| -- Decrement (prefix only) | |
| ? Binary operator, executes B if A is true, can be chained, evaluates left to right | |
| ?? Binary operator, tests if === N, can be chained, evaluates left to right | |
| ? : Ternary operator, can be chained, evaluates left to right | |
| ; evaluate expression on left, then on right, and return the one on right, chained | |
| 2.6 Operator Precedence | |
| Precedence Operators Notes | |
| 1 ++ -- Prefix only | |
| 2 ^ Exponentiation | |
| 3 * / % Multiplicative | |
| 4 + - Additive | |
| 5 ~! ~& ~| ~^ Bitwise | |
| 6 ! & | != Logical | |
| 7 == === : Equality and type-checking | |
| 9 ? ?? ? : Conditional and ternary flow | |
| 10 := += -= etc. Assignment (rightmost, non-chainable) | |
| 11 => Function return value assignment | |
| 12 ; Sequential evaluation | |
| 3. Control Flow | |
| 3.1 Expressions | |
| All statements are expressions, that can be composed to return a value | |
| (statement) | |
| 3.2 Compound Expressions | |
| Use parentheses and semicolons, executed in order, value is that of last expression | |
| (foo = 4; bar = 5; whatever()) | |
| 3.3 Blocks | |
| Compound Expressions are also Blocks, and have block scoped variable definitions. | |
| ( foo = 4 | |
| bar = 5 | |
| whatever() ) | |
| 3.4 Conditional Statements | |
| There is only one construct replacing if, else, switch, case etc. No ambiguities: | |
| expression ? ( | |
| foo = 4; bar = 5 | |
| ) : ( baz = 6; whatever() ) | |
| 3.5 Whitespace | |
| New lines may be indented by Tab which means creates an implicit block. | |
| Only one way to meaningfully indent: tabs, not spaces. | |
| expression ? | |
| foo = 4; bar = 5 | |
| : // newline+tab means start a block | |
| baz = 6 | |
| whatever | |
| // newline+old tab level means end the block | |
| 3. Definitions | |
| Identifiers must be more than one letter, and start with Unicode alphabetic characters | |
| This encourages developers to spend time entering meaningful names into their code | |
| And also easily presents naming conflicts with any keywords. Encourage underscore_style. | |
| 3.1 None | |
| This is the only keyword longer than 1 letter. | |
| There is no "undefined" state for a variable. | |
| Also, None with any operator above is still None, including comparison operators. | |
| 3.2 Variables | |
| All variables have to have a type. They are scoped to the block they are defined in. | |
| ( | |
| foo:I = 5, bar:S, // N by default | |
| bar + foo // N + anything doesn't throw error | |
| x:[I] = [0] * 20 * 40 // array of 800 elements | |
| y:[[I]] = x / 10 // 10 arrays of 80 elements each | |
| ) | |
| 3.3 Modifiers | |
| +M - Mutable / Modifiable | |
| -M - Immutable, can only be assigned to Immutable references | |
| +R - Remote / Retain / Release | |
| -R - Local, can only be assigned to other Local references | |
| +P - Pass by reference, even though result is not to be modified or retained | |
| -N - Cannot hold the value N (variables are nullable by default) | |
| 3.4 Inline Functions | |
| Types will be required if cannot be inferred from the context | |
| f (x, y) => some_expression_using(x, y) // return values by assigning | |
| 3.5 Named Functions | |
| f min(x:I, y:I = 0) | |
| min => x > y ? y : x; // return value by assigning to function's name | |
| 3.6 Destructuring Arrays | |
| f moo (x, y) = (x + y, x - y) | |
| [a, b] = moo(2, 3); | |
| 3.7 Destructuring Dictionaries | |
| f moo (x, y) = {a: x + y, b: x - y} | |
| {a, b} = moo(2, 3); | |
| 3.8 Static Variables in Functions | |
| f moo(x:I, y:S) | |
| moo = ++moo.bar; | |
| moo.bar = 5; | |
| 4. Functions | |
| 4.1 Definition Order | |
| Functions defined later in the file can be referenced earlier | |
| 4.2 Returning values | |
| Functions have a special operator that can be searched for in code, | |
| which assigns values to the function's return value: | |
| f example1(x):I | |
| x ? r // optional, return | |
| example1 => 5 | |
| f example2(x):I | |
| r => 5 | |
| f example3(x):I => 5 // inline lambda | |
| 4.3 Throwing Errors | |
| Errors are just functions that will be called on objects | |
| f example1(x):I | |
| x ? e => ErrorType.n(param1, param2) | |
| 4.4 Calling Functions | |
| Instead of try/catch, you can pass an object to receive Error messages. | |
| If the object doesn't handle this message, the stack keeps unwinding. | |
| f(x, y, z) : ErrorHandlerObject | |
| 4.5 Modifiers | |
| By default, parameters are not marked as +R (Remote) or +M (Mutable) | |
| f side_effects(x:I+R, y:S+R+M, z) // remote and mutable | |
| z = helper(z); // fine because helper x isn't +R | |
| y = 8; // error because it can't be modified | |
| a:I = 2 | |
| someGlobal = y // this is fine, points someGlobal to a proxy of y | |
| someGlobal = a // error because a is local | |
| someGlobal.setRemote(z) // error because z is local | |
| someGlobal.setRemote(z.c()) // this is fine, copy-on-write | |
| f helper(x) => x * 2; | |
| 4.6 Passing by Reference | |
| If a parameter is marked +M then it can be modified by the function | |
| If a parameter is marked as +R then it can be assigned anywhere. | |
| Either one would result in passing by reference in every call to the function. | |
| 5. Loops | |
| 5.1 Iterables | |
| Arrays and Dictionaries are iterable. Classes can also define iterators. | |
| 5.1 Generators | |
| f fib(x) ( | |
| fib = last_two + last_1; | |
| fib.last_one = fib; | |
| fib.last_two = fib.last_one; | |
| ) | |
| fib.last_one = 1; fib.last_two = 1; | |
| 5.2 For Loops | |
| generator.x(inline function) | |
| generator.x(function) | |
| array.x(inline function) | |
| dictionary.x(inline function) | |
| 5.3 While loops | |
| These just use a generator that always returns true, until you | |
| tell the loop to break and optionally return a value. | |
| result = w | |
| ++a; | |
| w = 4; | |
| a < 10 ? c : b => 4; // result = 4 in the end | |
| 6. Classes | |
| 6.1 Methods and Static Methods | |
| d MyClass : Superclass { | |
| f foo(x:I+R) () | |
| f bar(x:I+R) () | |
| } | |
| MyClass.staticFoo = f(x) = x*2; | |
| 6.2 Properties and Static Properties | |
| By default, parameters are not marked as +R (Remote) or -M (Imutable) | |
| d MyClass : Superclass { | |
| moo:I-M; // const int, not Modifiable | |
| remoteInt:I+R // remote, points to a proxy | |
| remoteClass:SomeClass+R; // remote, refers to | |
| internalStruct:SomeClass; // not remote, treated as inline struct | |
| } | |
| MyClass.staticMoo = 4; | |
| 7. Modifiers | |
| Assigning with different modifiers requires an explicit copy with .c() | |
| The only exception is assigning to a const (-M) if M is the only thing that changed. |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here is some example code: