// U Demo Program: “Ledger & Fibers”
// Showcases every language feature in a cohesive file.
/* Notes:
- Tabs only for indentation
- All statements are expressions
- Nested block comments work: /* inside here */ still fine
*/
/* 1) Globals and base values */
versionMajor:I := 1
versionMinor:I := 0
applicationName:S := "Ledger & Fibers"
noValue:N := N
userIdList:[I] := [1, 2, 3, 5, 8, 13]
weightList:[D] := [0.5, 1.5, 2.5]
idByName:{I} := {"alice": 1, "bob": 2}
matrixValues:[[I]] := [[1,2,3],[4,5,6],[7,8,9]]
userCount:I := userIdList.l()
matrixCopy:[[I]] := matrixValues.c()
maybeCount:I := noValue ?? userCount ?? 0
arithResult:I := (2 ^ 3 * 2 + 1 ; 7 * 3)
bitMask:I := (~| 0 ~| 0)
logicValue:L := !true | true & !false != false
isLedgerType:L := N : Ledger
/* 2) Modifiers */
initialBudget:D-M := 100.00
sharedRemotePool:D+R+M := 1000.00
localCounter:I-R := 0
mandatoryId:I-N := 1
globalRemoteValue:D+R
globalRemoteValue := sharedRemotePool
/* 3) Functions */
doubleValue := f(number) => number * 2
f clampValue(input:D, minValue:D := 0.0, maxValue:D := 1.0):D
clampValue => input < minValue ? minValue : (input > maxValue ? maxValue : input)
f addAndSubtract(first:I, second:I)
addAndSubtract => (first + second, first - second)
sumResult:I; diffResult:I
[sumResult, diffResult] := addAndSubtract(10, 3)
f calculateStats(values:[I])
totalSum:I := 0
values.x(f(item) => totalSum += item)
calculateStats => {sum: totalSum, count: values.l()}
{sum: totalUsers, count: totalCount} := calculateStats([2,3,5,7])
f nextUniqueId()
nextUniqueId := ++nextUniqueId.counterValue
nextUniqueId.counterValue := 100
/* 4) Errors */
d ErrorType {
f n(message:S, code:I) ()
}
f parseIntOrFail(stringValue:S):I
(stringValue == "bad") ? e => ErrorType.n("invalid int", 400)
parseIntOrFail => 42
d ConsoleErrorHandler {
f ErrorType(message:S, code:I) ()
}
f tryParseInt(stringValue:S):I
handlerObject := ConsoleErrorHandler.n()
resultValue:I := parseIntOrFail(stringValue) : handlerObject
resultValue === N ? 0 : resultValue
/* 5) Generators and .x */
f fibonacciGenerator()
fibonacciGenerator := fibonacciGenerator.previousValue + fibonacciGenerator.beforePrevious
fibonacciGenerator.beforePrevious := fibonacciGenerator
fibonacciGenerator.previousValue := fibonacciGenerator.beforePrevious
fibonacciGenerator.beforePrevious := 1; fibonacciGenerator.previousValue := 1
f collectFibonacciBelow(limitValue:I):[I]
fibList:[I] := []
indexValue:I := 0
w
nextFib:I := fibonacciGenerator()
(nextFib >= limitValue) ? b => fibList
fibList := fibList + [nextFib]
c
f dictionaryToPairs(dictValues:{I}):[[S]]
outputPairs:[[S]] := []
dictValues.x(f(pair)
isBig:L := pair.value > 10 ? true : false
outputPairs := outputPairs + [[pair.key, isBig ? "big" : "small"]]
)
dictionaryToPairs => outputPairs
/* Yield example: create an infinite sequence of even numbers */
f evenNumberGenerator()
counterValue:I := 0
w
y => counterValue
counterValue += 2
/* 6) Classes */
d Money {
amountValue:D-M
currencyCode:S-M
f n(amount:D, currency:S) ()
f addAmount(extra:D):Money ()
Money => Money.n(amountValue + extra, currencyCode)
}
Money.zeroUSD := Money.n(0.0, "USD")
Money.addAll := f(moneyList:[Money]) => moneyList.l() == 0 ? Money.zeroUSD : (
totalAmount:D := 0.0;
moneyList.x(f(moneyItem) => totalAmount += moneyItem.amountValue);
Money.n(totalAmount, moneyList[0].currencyCode)
)
d Ledger {
titleText:S-M
totalAmount:D-M
remoteMirror:D+R
entryList:[[S]]
f n(title:S, startAmount:D) ()
f postEntry(entryDate:S, entryMemo:S+R) ()
entryList := entryList + [[entryDate, entryMemo]]
Ledger => title
f balance():Money ()
Ledger.balance => Money.n(totalAmount + remoteMirror, "USD")
}
Ledger.createUSD := f(title:S, startAmount:D):Ledger
(
ledgerInstance:Ledger := Ledger.n(title, startAmount);
ledgerInstance.remoteMirror := startAmount * 0.0;
ledgerInstance
)
f isLedgerInstance(value):L
value : Ledger ? true : false
/* 7) Remote rules */
globalLedger:Ledger+R
f setGlobalLedger(ledgerValue:Ledger+R)
globalLedger := ledgerValue
f linkRemotePoolToLedger(ledgerValue:Ledger+R, poolValue:D+R)
ledgerValue.remoteMirror := poolValue
linkRemotePoolToLedger => ledgerValue
f addEntryAndClampAmount(ledgerValue:Ledger+R, entryDate:S, entryMemo:S+R, deltaAmount:D):Money
clampedAmount:D := clampValue(deltaAmount, -1000.0, 1000.0)
resultMoney:Money := ledgerValue.postEntry(entryDate, entryMemo).balance()
(resultMoney ; Money.n(resultMoney.amountValue + clampedAmount, resultMoney.currencyCode))
/* 8) Arrays & math */
f sumMatrixRow(matrixData:[[I]], rowIndex:I):I
rowSum:I := 0
matrixData[rowIndex].x(f(value) => rowSum += value)
sumMatrixRow => rowSum
f exponentMix(first:I, second:I):I
exponentMix => 2 ^ first * second + 1
/* 9) While-generator */
f sumValuesUnderLimit(values:[I], limit:I):I
accumulator:I := 0
indexValue:I := 0
w
indexValue >= values.l() ? b => accumulator
currentValue:I := values[indexValue]; ++indexValue
currentValue >= limit ? c : 0
accumulator += currentValue
/* 10) Error in practice */
f recordAmountFromString(stringValue:S, ledgerValue:Ledger+R):Money
parsedAmount:I := tryParseInt(stringValue)
addEntryAndClampAmount(ledgerValue, "2025-08-13", "parsed", parsedAmount)
/* 11) Main */
f main():I
operationsLedger:Ledger := Ledger.createUSD("Ops", 10.0)
setGlobalLedger(operationsLedger)
linkRemotePoolToLedger(operationsLedger, sharedRemotePool)
[plusValue, minusValue] := addAndSubtract(8, 5)
{sum: sumUsers, count: countUsers} := calculateStats(userIdList)
fibNumbers:[I] := collectFibonacciBelow(200)
dictPairs:[[S]] := dictionaryToPairs({"a": 1, "b": 22, "c": 5})
moneyOne:Money := Money.n(12.5, "USD")
moneyTwo:Money := moneyOne.addAmount(7.5)
moneyTotal:Money := Money.addAll([moneyOne, moneyTwo])
bookedOne:Money := recordAmountFromString("bad", operationsLedger)
bookedTwo:Money := recordAmountFromString("123", operationsLedger)
sumSmallValues:I := sumValuesUnderLimit([1,2,50,3,4], 10)
secondRowSum:I := sumMatrixRow(matrixValues, 1)
mixedValue:I := exponentMix(3, 2)
fibCount:I := fibNumbers.l() ?? 0
sumFibs:I := 0
fibNumbers.x(f(numValue) => sumFibs += numValue)
isLedgerInstance(operationsLedger) ?
sumFibs += 1
:
sumFibs += 0
// Use yield-based even number generator for first 5 evens
firstEvens:[I] := []
evensGen := evenNumberGenerator()
counterIndex:I := 0
w
counterIndex >= 5 ? b => firstEvens
firstEvens := firstEvens + [evensGen.y()]
counterIndex += 1
main => (fibCount + secondRowSum + mixedValue + sumFibs + sumUsers + countUsers)
CureYooz•14h ago
How about an example that is actually readable and easy to follow?
EGreg•11h ago
Here, I will write one by hand right now:
d IntegerMath : SomeInterface, SomeOtherInterface
someProperty:I
someOtherProperty:I-M
referenceProperty:IntegerMath+R+N // nullable
recursiveProperty:IntegerMath+N // error! needs +R
f stuff(myObj: IntegerMath+M+R, newValue: I+M)
newValue = 4 // modifies caller's reference
myObj.someProperty = newValue // error, need newValue.c()
myObj.someOtherProperty = 4 // error, immutable property
IntegerMath.max = f(first:I, second:I):I
r => first > second ? first : second
IntegerMath.min = f(first:I, second:I):I => first < second ? first : second
// let's use the class
im = IntegerMath.n({someOtherProperty: 4}) // infers im:IntegerMath
im.someProperty = 3
im.stuff(im, someInteger) // modifies both
result = Math.sort([1,2,3], IntegerMath.max) // infers result:[I]
another = Math.sort(result, f(first:I, second:I):I => first > second ? first : second)
// conditional code
[1,2,3].x( f(k)
k > 2
? console.log(k)
: (
result = k
console.log(k-2)
)
// this is a closure but doesn't assign
// anything to +R variables, so it's not +R itself
// in Swift language, it's not "escaping"
Someone•14h ago
f addAndSubtract(first:I, second:I)
addAndSubtract => (first + second, first - second)
sumResult:I; diffResult:I
[sumResult, diffResult] := addAndSubtract(10, 3)
Weird choice to create a tuple with (a,b), but destructure it with [a,b], even more so given
There, creating and destructuring a hash use matching syntax {}
I also don’t like single character names for length, iteration, and don’t understand the type system. :I is an integer, but :I-R? I can’t figure out what that means from this example.
EGreg•11h ago
Yeah, I shouldn't have used ChatGPT to create the example I posted. It still messes up (since it wasn't trained on the language at all, of course). I will fix up the examples, but it should be very obvious and clear, never "weird"... see the spec in the link.
Basically, -R is the default anyway everywhere. +R means "remote" or "retained", i.e. the variable doesn't live on the stack. By default, parameters passed to functions are -R and can only be assigned to other -R variables. If you try to assign a -R to a +R variable or its property, you have to do .c() to mark it to be potentially copied to the heap. This is to explicitly prevent use-after-free errors or dangling pointers.
Basically, +M on a parameter to a function means it can be modified, it becomes passed by reference. +M with or without +R is just modifying the reference in-place, but without +R you can't assign it to any +R variable or its property without calling .c() on it. The c() marks it as copy-on-write which makes a copy if its destructor runs and it still has references to it. If it only has one reference to it, then obviously we avoid making a copy on write.
In functions, -M is the default for parameters, but in classes, +M is the default for properties (mutable) and you have to mark it -M explicitly (like const keyword).
Also +N means something can be nullable, which is another set of explicit optimizations (dereferencing a variable without a guard gives a compile-time error if it's nullable).
A class is basically a struct by default, i.e. all properties are local (-R is implicit) but if you mark some properties +R then they become pointers to other areas in memory. You might want to do this if e.g. multiple objects have properties that reference the same objects.
Also, unlike Rust which has only one writer, we have the idea of transparent proxy objects, which can use various MVCC strategies (e.g. caching an old value) to avoid concurrent race conditions. In this way it is even designed to support decentralized code, across multiple machines.
The language is designed to catch a wide set of mistakes (typing = when you meant ==, etc.) and let you inspect retaining graphs for memory leaks and much more. In short, it is supposed to be easier to grok than Rust, and in some cases even safer, without needing borrow checkers etc.
Someone•10h ago
> +R means "remote" or "retained", i.e. the variable doesn't live on the stack
EGreg•14h ago
CureYooz•14h ago
EGreg•11h ago
Someone•14h ago
I also don’t like single character names for length, iteration, and don’t understand the type system. :I is an integer, but :I-R? I can’t figure out what that means from this example.
EGreg•11h ago
Basically, -R is the default anyway everywhere. +R means "remote" or "retained", i.e. the variable doesn't live on the stack. By default, parameters passed to functions are -R and can only be assigned to other -R variables. If you try to assign a -R to a +R variable or its property, you have to do .c() to mark it to be potentially copied to the heap. This is to explicitly prevent use-after-free errors or dangling pointers.
Basically, +M on a parameter to a function means it can be modified, it becomes passed by reference. +M with or without +R is just modifying the reference in-place, but without +R you can't assign it to any +R variable or its property without calling .c() on it. The c() marks it as copy-on-write which makes a copy if its destructor runs and it still has references to it. If it only has one reference to it, then obviously we avoid making a copy on write.
In functions, -M is the default for parameters, but in classes, +M is the default for properties (mutable) and you have to mark it -M explicitly (like const keyword).
Also +N means something can be nullable, which is another set of explicit optimizations (dereferencing a variable without a guard gives a compile-time error if it's nullable).
A class is basically a struct by default, i.e. all properties are local (-R is implicit) but if you mark some properties +R then they become pointers to other areas in memory. You might want to do this if e.g. multiple objects have properties that reference the same objects.
Also, unlike Rust which has only one writer, we have the idea of transparent proxy objects, which can use various MVCC strategies (e.g. caching an old value) to avoid concurrent race conditions. In this way it is even designed to support decentralized code, across multiple machines.
The language is designed to catch a wide set of mistakes (typing = when you meant ==, etc.) and let you inspect retaining graphs for memory leaks and much more. In short, it is supposed to be easier to grok than Rust, and in some cases even safer, without needing borrow checkers etc.
Someone•10h ago
I think that’s a bad name. Both “remote” (https://en.wikipedia.org/wiki/Remote_procedure_call) and “retained” (https://en.wikipedia.org/wiki/Retained_mode, https://developer.apple.com/documentation/objectivec/nsobjec...) are existing terms in programming with wildly different meaning. What’s wrong with “reference” (https://en.wikipedia.org/wiki/Reference_(computer_science)) ?
EGreg•10h ago
It’s just a letter after all
But you should be able to retain/release this variable, and it is not on the stack
I was considering +G for GPU SIMD vectors, but didnt know if I need to