Risor vs Expr
Expr is a popular expression language for Go. It's fast, type-checks against host structs at compile time, and guarantees termination. It powers Argo Workflows, WunderGraph, and many production applications. Developers consistently report being "up and running in just a few minutes."
Expr evaluates single expressions. Risor adds variables, functions, pattern matching, and pipe operators in TypeScript-like syntax.
Where Expressions Run Out of Room
Expr (everything in one expression with ternaries):
(order.Total > 1000 ? 0.15 : (order.Total > 500 ? 0.10 : 0.05))
* order.Total
* (order.Customer.Tier == "enterprise" ? 0.9 : 1.0)
Risor:
let discount = if (order.total > 1000) { 0.15 }
else if (order.total > 500) { 0.10 }
else { 0.05 }
let multiplier = if (order.customer.tier == "enterprise") { 0.9 } else { 1.0 }
order.total * (1 - discount) * multiplierRisor also supports user-defined functions. Expr doesn't:
function eligible(user) {
user.active && user.age >= 18 && user.verified
}
function discount_rate(user) {
match user.tier {
"enterprise" => 0.20
"pro" => 0.10
_ => 0.0
}
}
let rate = if (eligible(user)) { discount_rate(user) } else { 0.0 }
order.total * (1 - rate)Comparison
| Property | Expr | Risor |
|---|---|---|
| Scope | Single expressions | Multi-statement scripts |
| Variables / functions | No | Yes |
| Pattern matching / pipes | No | Yes |
| Sandboxed | Type-checked against provided env | Empty sandbox by default |
| Syntax | Go-like | TypeScript-like |
| Feature control | Fixed | Host chooses which features to enable |
| Type integration | Compile-time checking against Go structs | Native host types via environment |
| License | MIT (with commercial "Pro" tier) | Fully open source |
Performance
Expr is the fastest runtime in our benchmarks for simple expressions: 0.08 µs versus Risor's 15.2 µs. Its compiled expression tree has almost no runtime dispatch.
Expr can also handle data processing, filtering and aggregating 10,000 records in 853 µs versus Risor's 4.20 ms. But Expr hits its ceiling at multi-step logic: no variables, no multi-statement scripts. Our business logic benchmark (two filters, a map, a reduce, and a conditional) can't be written in Expr at all.
Memory footprints are nearly identical for simple expressions: Expr at 1.9 KB, Risor at 2.2 KB.
| Scenario | Expr | Risor |
|---|---|---|
| Simple expression (warm) | 0.08 µs | 15.2 µs |
| Data processing (10k records) | 853 µs | 4.20 ms |
| Business logic (500 records) | N/A | 273 µs |
| Heap per instance (simple) | 1.9 KB | 2.2 KB |
When to Choose Expr
- Your logic fits in single expressions
- You want compile-time type checking against Go structs
- You want the most minimal integration surface
When to Choose Risor
- You need variables, functions, or multi-step computation
- You want TypeScript-like syntax and pipe operators
- You want the host to control which language features are available
- You want fully open source with no commercial tier