r/lua • u/S3kshun8-OMW • 13h ago
Project JSL: I started optimizing Lua and accidentally built a language dialect
This entire adventure started with an obsession over Lua performance.
It has since escalated into maintaining a custom Luau dialect.
Oops.
I'm the developer of Jess, an open-source, cross-platform application runtime built on Luau, powered by a Rust binding framework called L3i.
Jess uses JSL (.jsl) as its primary scripting language. The familiar Luau foundation is still there: gradual typing, strict checking, tables, functions, and all the usual conveniences.
But I wanted to see how far we could push the language without losing the things that make Lua attractive in the first place.
And, crucially, without making it slower.
First, the boring part.
A complete Jess script looks like this:
```luau --!strict local log = require('@jess/log').new 'hello'
print(Hello from Jess {jess.version}.)
log.info 'ready'
```
Run it with:
bash
jess run hello.jsl
Nothing particularly exciting. It's Luau, running outside Roblox, with access to a set of native APIs, a type checker, a module system, and a CLI.
That's the baseline.
Now let's make things interesting.
Comprehensions, reducers, and generators
One of my favorite additions is Python-inspired comprehensions, with a slightly different approach to their syntax and execution.
```luau -- Filter and project local ids = [for e in entities if e.active => e.id]
-- Sum without constructing an intermediate array local mass = sum[for body in bodies => body.mass]
-- Short-circuiting boolean reduction local heavy = any[for body in bodies => body.mass > 100]
-- Pairwise iteration, requiring equal lengths local totals = [for a, b in zipStrict(xs, ys) => a + b] ```
We support nested generators, filters, range, enumerate, zipShortest, and zipStrict, along with sum, min, max, any, and all reducers.
An ordinary comprehension produces a dense, 1-based array. Reducers operate directly on the generated values without constructing intermediate tables.
The important part is how these expressions compile.
They're lowered into ordinary Luau loops.
No iterator objects. No callback per element. No intermediate arrays unless the operation actually requires one.
In our instruction-count benchmarks, the resulting loops execute extremely close to equivalent handwritten implementations. There are explicit semantic checks, such as rejecting nil projections, but we've been careful to measure their costs rather than simply assume the syntax is free.
Slices and destructuring
We've also added slicing and record patterns.
```luau -- Inclusive, 1-based slicing local sample = values[1:16]
-- Destructure a record local {x, y, z} = position
-- Rename fields local {health: hp, name} = entity
-- Nested destructuring local {transform: {position: pos, rotation}} = entity ```
Patterns work in local declarations, function parameters, and comprehension generators.
They lower to ordinary local bindings and field accesses, preserving Luau's normal indexing behavior, including metamethods.
There isn't a new object model lurking underneath this. That's intentional.
And because apparently that wasn't enough...
The dialect also supports typed buffer slices and sinks.
For example:
```luau -- Read f32 values from 16-byte records local mass = sum[for value in bodies[1:n, "f32@16"] => value]
-- Reuse an existing table as output storage into(scratch)[for x in values if x > 0 => x * 2] ```
This is particularly interesting for the kinds of workloads I've been experimenting with: processing binary formats, large datasets, game assets, and other situations where allocating thousands of temporary Lua objects isn't particularly appealing.
Certain recognized buffer reductions can even dispatch directly into L3i's native bulk-data operations instead of executing a scalar loop.
This is where the relationship between the language and the runtime starts getting interesting.
But isn't this just syntactic sugar?
In many cases, yes!
And I'm entirely comfortable with that.
The interesting engineering problem isn't necessarily inventing fundamentally new operations. It's providing convenient ways to express existing operations while preserving predictable behavior, efficient execution, and useful tooling.
JSL doesn't require a permanent fork of Luau's compiler.
Instead, L3i implements a source frontend that recognizes the extended syntax, lowers it into ordinary Luau, and maintains source mappings so diagnostics and analysis refer to the code you actually wrote.
The resulting program still goes through Luau's compiler and type checker.
That means we get to experiment with language features without maintaining an entirely separate compiler backend or type system.
It's also meant a rather absurd amount of work ensuring that the resulting code behaves exactly as intended. Evaluation order, short-circuiting, error conditions, type inference, and performance all matter.
A language feature that makes code prettier but quietly destroys performance isn't particularly attractive to me.
So why build all this?
Initially? Performance.
I started experimenting with moving native applications and tools into Luau, then became increasingly interested in what could be accomplished with a relatively small native runtime and a much larger programmable environment.
That eventually produced L3i, JSL, and Jess.
Jess itself has grown into something much broader: applications, packages, workspaces, capability-controlled execution, and infrastructure for tools and games.
But the underlying motivation remains the same.
How much work can we move into Lua without giving up the performance and control that traditionally push developers toward native languages?
I don't think the answer is necessarily to turn Lua into Python or Rust. I like Lua precisely because it isn't those languages.
I do, however, think there are useful ideas worth borrowing.
Some come from Python. Some come from Rust. Some come from staring at a perfectly reasonable syntax decision and deciding that it simply isn't offensive enough.
And I'm perfectly willing to throw an idea away if the measurements don't justify keeping it.
For anyone interested in the implementation, the L3i dialect documentation covers the lowering model, semantics, source mappings, and performance measurements in detail.
Jess documentation: https://dreamweave-mp.github.io/jess/docs/
Both projects are open source under MIT/Apache-2.0. They're still evolving, and I'd welcome technically grounded criticism.
So here's a question.
What's one feature from another programming language that you desperately wish Lua or Luau had?
And, more importantly, what would it actually buy us?
I'm especially interested in features that improve expressiveness without imposing significant runtime costs, or that let us express operations in ways a compiler can optimize more effectively than conventional function-based APIs.
Tell me what you'd add. Tell me what you'd absolutely refuse to add.
Bring your worst ideas. I have a compiler and questionable impulse control.