Data Paradigms in TCL: Associative Arrays vs Dictionaries

Working with TCL for a while, you have to come to terms with the fact that arrays and dicts aren't just different APIs. They're fundamentally different beasts under the hood.

The Implementation Reality

Associative arrays are TCL's original key-value store, implemented as hash tables directly in the interpreter. When you write set arr(key) value, you're actually creating a variable with a compound name. The interpreter maintains a separate hash table for each array variable, and accessing $arr(key) triggers a hash lookup on that specific table.

Dictionaries came later (TCL 8.5 in 2007) as first-class values. Unlike arrays, a dict is just a string with a specific internal representation: a list of alternating keys and values that gets cached as a hash table when you perform dict operations on it. The key insight is that dicts are values that can be passed around, while arrays are variables that live in specific scopes.

Why This Matters in Practice

Arrays tie you to variable scopes. You can't return an array from a procedure without using upvar or global to work around the limitation. Arrays also can't be nested without ugly naming tricks like set arr(outer,inner) value.

# Arrays: scope-bound and flat
proc make_config {} {
    # Can't return this directly
    set config(host) "localhost"
    set config(port) 8080
}

# Dicts: values you can actually use
proc make_config {} {
    return [dict create host localhost port 8080]
}

Dicts shine for structured data and functional-style programming. Need nested data? dict set config database host localhost just works. Want to pass complex data between procedures? Dicts are your friend.

Performance Quirks

Here's the counterintuitive part: arrays can be faster for simple key-value operations because there's no string parsing overhead (everything is a string, except when it's not). But dicts win for complex operations because they can optimize their internal representation and handle nesting efficiently.

Arrays also support pattern matching with array names pattern, which dicts can't match without iteration.

When to Use What

Use arrays for:

  • Simple key-value stores that stay in one scope
  • When you need pattern matching on keys
  • Legacy code that expects array semantics

Use dicts for:

  • Structured, nested data
  • Passing data between procedures
  • Modern TCL code that values composability

The real lesson? TCL's "everything is a string" philosophy is surface-level, and it means these data structures evolved different internal optimizations while maintaining the same string-based interface. At FlightAware, we've learned this the hard way. Legacy code is full of arrays that should have been dicts, and newer code sometimes uses dicts where arrays would be simpler. Understanding the implementation helps you pick the right tool and avoid performance traps.