Immutable Tables with Functional Operations
What are Immutable Tables?
Immutable tables are data structures that cannot be modified after creation. All operations on tables return new tables rather than modifying the original.
/* All table operations return new tables */
original : {a: 1, b: 2, c: 3};
modified : t.set original "d" 4; /* Returns new table */
/* original is unchanged: {a: 1, b: 2, c: 3} */
/* modified is: {a: 1, b: 2, c: 3, d: 4} */
Why is This Esoteric?
Most programming languages allow direct modification of data structures. Our language enforces complete immutability - no mutation operations exist at all.
Basic Examples
/* Create a table */
original : {name: "Alice", age: 30, city: "New York"};
/* All operations return new tables */
with_job : t.set original "job" "Engineer";
with_updated_age : t.set original "age" 31;
without_city : t.delete original "city";
/* Original table is unchanged */
/* original is still {name: "Alice", age: 30, city: "New York"} */
Table Operations
Setting Values
/* t.set table key value - returns new table with key set */
data : {a: 1, b: 2};
updated : t.set data "c" 3;
/* updated: {a: 1, b: 2, c: 3} */
/* data: {a: 1, b: 2} (unchanged) */
Deleting Keys
/* t.delete table key - returns new table without the key */
data : {a: 1, b: 2, c: 3};
without_b : t.delete data "b";
/* without_b: {a: 1, c: 3} */
/* data: {a: 1, b: 2, c: 3} (unchanged) */
Merging Tables
/* t.merge table1 table2 - returns new table with combined keys */
table1 : {a: 1, b: 2};
table2 : {c: 3, d: 4};
merged : t.merge table1 table2;
/* merged: {a: 1, b: 2, c: 3, d: 4} */
/* table1 and table2 unchanged */
Getting Values
/* t.get table key - returns value (doesn't modify table) */
data : {name: "Alice", age: 30};
name : t.get data "name"; /* "Alice" */
age : t.get data "age"; /* 30 */
/* data unchanged */
Checking Keys
/* t.has table key - returns boolean (doesn't modify table) */
data : {name: "Alice", age: 30};
has_name : t.has data "name"; /* true */
has_job : t.has data "job"; /* false */
/* data unchanged */
Element-Wise Operations
All element-wise operations return new tables:
/* map returns new table - @ operator required for higher-order functions */
numbers : {a: 1, b: 2, c: 3};
double : x -> x * 2;
doubled : map @double numbers; /* {a: 2, b: 4, c: 6} */
/* numbers unchanged: {a: 1, b: 2, c: 3} */
/* filter returns new table - @ operator required for higher-order functions */
is_greater_than_one : x -> x > 1;
filtered : filter @is_greater_than_one numbers; /* {b: 2, c: 3} */
/* numbers unchanged: {a: 1, b: 2, c: 3} */
Complex Examples
/* Building complex tables immutably */
base_user : {name: "Alice", age: 30};
/* Add multiple properties */
with_email : t.set base_user "email" "alice@example.com";
with_address : t.set with_email "address" "123 Main St";
with_phone : t.set with_address "phone" "555-1234";
/* Or merge with another table */
contact_info : {email: "alice@example.com", phone: "555-1234"};
complete_user : t.merge base_user contact_info;
/* Result: {name: "Alice", age: 30, email: "alice@example.com", phone: "555-1234"} */
Nested Tables
Immutability works with nested table structures:
/* Nested table */
user : {
name: "Alice",
profile: {
age: 30,
preferences: {
theme: "dark",
notifications: true
}
}
};
/* Update nested property - creates new nested structure */
updated_preferences : t.set user.profile.preferences "theme" "light";
/* This creates new tables at each level */
/* user unchanged, updated_preferences has new nested structure */
Functional Programming Patterns
Immutability enables pure functional programming patterns:
/* Pure function - no side effects */
update_age : user new_age -> t.set user "age" new_age;
/* Multiple updates create new tables */
user1 : {name: "Alice", age: 30};
user2 : update_age user1 31;
user3 : update_age user2 32;
/* All tables exist independently */
/* user1: {name: "Alice", age: 30} */
/* user2: {name: "Alice", age: 31} */
/* user3: {name: "Alice", age: 32} */
When to Use Immutable Tables
Use immutable tables when:
- You want to prevent accidental data modification
- You're building functional programming patterns
- You need to track data changes over time
- You want to ensure thread safety (if applicable)
- You're working with complex data transformations
Don't use immutable tables when:
- You need to modify data in place for performance reasons
- You're working with very large datasets that can't be copied
- You need to perform side effects on data structures
Common Patterns
/* Pattern 1: Building up data structures */
base_config : {debug: false, timeout: 30};
/* Add development settings */
dev_config : t.merge base_config {
debug: true,
log_level: "verbose"
};
/* Add production settings */
prod_config : t.merge base_config {
timeout: 60,
cache_enabled: true
};
/* Pattern 2: Data transformation pipeline */
user_data : {name: "Alice", age: 30, scores: {85, 90, 88}};
/* Transform user data */
with_average : t.set user_data "average_score" (reduce @add 0 user_data.scores / 3);
with_grade : t.set with_average "grade" (when with_average.average_score is
when with_average.average_score >= 90 then "A"
when with_average.average_score >= 80 then "B"
_ then "C");
/* Pattern 3: State management */
initial_state : {count: 0, items: {}};
/* State transitions */
increment_state : state -> t.set state "count" (state.count + 1);
add_item_state : state item -> t.set state "items" (t.set state.items item.id item);
/* Apply transitions */
state1 : increment_state initial_state;
state2 : add_item_state state1 {id: "item1", name: "First Item"};
Performance Considerations
/* Immutability can be expensive for large tables */
large_table : {/* ... many entries ... */};
/* Each operation creates a new copy */
updated1 : t.set large_table "key" "value";
updated2 : t.set updated1 "key2" "value2";
/* This creates multiple copies of the large table */
/* Consider batching operations */
batch_update : table -> t.merge table {
key1: "value1",
key2: "value2",
key3: "value3"
};
/* Single operation instead of multiple */
Key Takeaways
- Complete immutability - no mutation operations exist
- New tables returned - all operations return new data structures
- Original unchanged - source tables are never modified
- Functional patterns - enables pure functional programming
- Composable operations - operations can be chained safely
- @ operator required - for higher-order functions like
map
,filter
,reduce
Why This Matters
Immutable tables make the language safer and more functional:
- No side effects - functions can't accidentally modify data
- Predictable behavior - data never changes unexpectedly
- Functional style - encourages pure functions and composition
- Debugging ease - data state is always predictable
- Thread safety - no shared mutable state issues
This feature makes the language feel more like pure functional languages like Haskell! 🚀