⇦
2026-04-24
I have a quarter baked language I've been working on. It's mostly crap, but a syntax idea fell out that I think is pretty neat.
The following are equivalent in many languages:
x = x + 1
x += 1
Reassigning is cool as it's lexically scoped so easy to reason about (mutation is bad as it isn't).
The new idea is that instead of special symbols (in this case +=) we generalise with a keyword (alt) that affects all infix operators, so the following are equivalent:
x = x + 1
alt x + 1
Who cares? It's not even shorter right?
The fun starts when we introduce a couple of new infix operators - namely ]= and .=
Let's compare them to the Python equivalents:
l = l[4]=999
l = l[:4] + [999] + l[5:]
and:
x = x.n.=2
x = dataclasses.replace(x, n=2)
Now let's set up a couple of examples of nested data:
cat = Cat(age=3)
l = [1, [2, cat], 4]
If we want to reassign to make an older cat, we can do:
alt cat.age.=8
The more interesting example is reassigning the deeply nested l to make the cat inside older, without mutating the original cat:
alt l[1][1].age.=9
this will leave us with l equal to:
[1, [2, Cat(age=9)], 4]
What was the alt statement sugar for, such that we didn't mutate the original cat? Well, the rather ungainly:
_1 = l[1] # _1 = [2, Cat(age=3)]
_2 = l[1][1] # _2 = Cat(age=3)
_2 = _2.age.=9 # _2 = Cat(age=9)
_1 = _1[1]=_2 # _1 = [2, Cat(age=9)]
l = l[1]=_1 # l = [1, [2, Cat(age=9)], 4]
My new language has a nice(?) feature - you can infix plain binary functions with tildes - this means you can do fun stuff like:
alt l~push~5
to leave l equal to:
[1, [2, Cat(age=9)], 4, 5]
Thoughts
- I don't like mutations, I do like being able to succinctly update deeply nested data. This syntax lets you achieve cuteness in a language with only immutable datastructures.
- What do they do in Haskell? Are there any other equivalent syntaxes lying around?
- The rough plan for my language was - pretend all the datastructures are immutable, but at compile time, if the structure of the program is such that using a mutable datastructure would be equivalent, use one for performance. (This is kind of the reverse of mutable value semantics in Swift(?), and would share some implementation details with the borrow checker in Rust).
- In some ways, this is the reverse of some older thoughts.
- In my langauge, I also added a generalised version of Rust's
?operator.