Symbol Noise
How many special characters clutter each line of code?
"Symbol noise" (formally: sigil density) counts non-alphanumeric, non-whitespace characters per line. Think: & * < > :: -> => ? ! ; : { } [ ].
Why it matters
Every special character is a micro-decision for the reader: what does this symbol mean in this context? Languages that overload symbols (& means reference AND bitwise-AND AND pattern-binding in Rust) impose more cognitive work per character.
Results
| Language ↕ | Avg Symbols/Line ↑ | Avg Unique Symbol Types ↕ |
|---|---|---|
| Csharp | ★3.8▼ | ★14▼ |
| Go | ★4.2▼ | ★16▼ |
| Ruby | ★4.3▼ | ★14▼ |
| Swift | ★4.3▼ | ★15▼ |
| Haskell | ★4.4▼ | ★15▼ |
| Kotlin | ★4.6▼ | ★15▼ |
| Milo | ★4.9▼ | ★16▼ |
| Python | ★4.9▼ | ★12▼ |
| C | ★5.3▼ | ★21▼ |
| Clojure | ★5.3▼ | ★8▼ |
| Java | ★5.6▼ | ★17▼ |
| Elixir | ★5.7▼ | ★15▼ |
| Javascript | ★5.8▼ | ★15▼ |
| Rust | ★5.9▼ | ★17▼ |
| Cpp | ★6.2▼ | ★19▼ |
| Typescript | ★6.2▼ | ★17▼ |
| Zig | ★6.4▼ | ★19▼ |
| Objc | ★6.6▼ | ★19▼ |
| Erlang | ★6.7▼ | ★17▼ |
The two dimensions of noise
Symbols per line (density) — how cluttered does each line look?
Unique symbol types (vocabulary) — how many different squiggles do you need to learn?
These don't always correlate. Haskell has low density (4.6/line) AND low vocabulary (14 types) — few symbols used sparsely. Elixir has high density (6.4/line) but moderate vocabulary (15 types) — a few symbols (|>, &) used heavily.
Why each language is noisy (or not)
Rust's noise is safety-related. The &, mut, Some(), unwrap(), lifetime annotations — they encode ownership and borrowing. Each symbol carries real semantic weight.
TypeScript's noise is syntactic. Generics (<number, number>), non-null assertion (!), optional chaining (?.) — these serve the type checker, not runtime behavior.
Elixir's noise is pipeline-driven. The |> operator, pattern matching ({:ok, val}), anonymous functions (&(&1 + 1)) — dense but consistent.
Go avoids symbols by using words. make() instead of {}, append() instead of .push(). Lower symbol density, but more lines of code.
Ruby minimizes both. Blocks (do |x| ... end), English-like methods (empty?, puts, each), implicit returns — code reads almost like prose.
Key insight
Cognitive load scales with symbol variety (how many different symbols to learn), not symbol frequency (how often they appear). Seeing &&& is fine if & always means one thing. Seeing & &mut &'a *const *mut is five concepts in similar-looking syntax — that's expensive.
What counts as a symbol?
Everything that isn't a letter, digit, or whitespace: + - * / % = == != < > <= >= && || ! ~ ^ & | ; : , . :: -> => .. ..= ? # @ ' " ( ) [ ] { } < >
We count both total symbols per line (density) and unique symbol types per program (vocabulary).