These are JBR centric, YMMV
Claude Code SKILL.md for Tcl. This is a Markdown doc so we'll just call it code for now
---
name: tcl-scripting
description: Develop Tcl scripts, Tk desktop GUIs, and critcl C extensions. Use when working on .tcl files, writing procs, creating Tk interfaces, binding events, building C extensions, or debugging Tcl errors. Triggers on tcl, tclsh, wish, tk, proc, namespace, critcl, widget, grid, pack, bind, expr, lmap, dict.
allowed-tools:
- Read
- Edit
- Write
- Grep
- Glob
- Bash
---
# Tcl Development
## Core Philosophy
Tcl's power comes from "everything is a string" - but this requires defensive coding:
1. **Security first**: Unbraced `expr` and unquoted command building are injection vectors
2. **Explicit over implicit**: Brace expressions, use `[list]` for commands, namespace your code
3. **Data as code**: Tcl's metaprogramming enables DSLs and introspection - embrace it thoughtfully
## Decision Frameworks
### Quoting: When to Use What
- `{braces}` - Literal text, no substitution, defer evaluation (proc bodies, expr)
- `"quotes"` - Substitution needed but preserve as single word
- `bare` - Simple values only, avoid for user input or complex strings
### Geometry Manager: grid vs pack
- **grid** - Multi-column layouts, forms, tables, alignment matters
- **pack** - Simple stacking, toolbars, single-direction flow
### Pure Tcl vs Critcl Extension
- **Pure Tcl** - Business logic, GUIs, scripting, prototyping
- **Critcl** - System bindings (X11, hardware), performance-critical loops, existing C libraries
### Collection Operations: foreach vs lmap
- **lmap** - Transforming lists (returns new list)
- **foreach** - Side effects, early exit, multiple lists
## Anti-Patterns
- **Unbraced expr**: `expr $x + $y` is slow and dangerous. Attacker-controlled `$x` can execute code. Always `expr {$x + $y}`.
- **String-built commands**: `eval "cmd $arg"` breaks on spaces/special chars. Use `cmd $arg` directly or `{*}[list cmd $arg]`.
- **Global state**: Variables in global namespace collide across packages. Use `namespace eval` with `variable` declarations.
- **Inline bind logic**: `bind .b <1> { complex; multi; line; code }` is unmaintainable. Extract to named procs.
- **Silent catch**: `catch {cmd}` swallows errors invisibly. Always check result: `if {[catch {cmd} err]} { handle $err }`.
- **pack forget/grid forget loops**: Repeatedly hiding/showing widgets is slow. Use `grid remove` to preserve config, or redesign with frames.
## Integration Points
- **Testing**: Structure tests as procs returning boolean. Use tcltest package for larger suites.
- **C extensions**: critcl compiles inline C. Separate concerns: C for computation, Tcl for glue.
- **Shell integration**: Tcl excels at process coordination via `exec`, `open |pipe`, `fileevent`.
## References
- Tcl Manual: https://www.tcl-lang.org/man/
- Tcl Wiki: https://wiki.tcl-lang.org
- critcl: https://andreas-kupries.github.io/critcl/
- Style guide: https://wiki.tcl-lang.org/page/Tcl+Style+GuideClaude Code tcl-pro SKILL for more depth
---
name: tcl-pro
description: Master Tcl philosophy and advanced idioms. Use when writing idiomatic Tcl, metaprogramming, functional patterns, or optimizing data structure choices. Triggers on EIAS, everything is a string, homoiconic, uplevel, upvar, lmap, fold, K combinator, ensemble, coroutine, functional Tcl, list vs dict vs array.
allowed-tools:
- Read
- Edit
- Write
- Grep
- Glob
- Bash
---
# Tcl Pro: Philosophy and Advanced Idioms
## Core Philosophy
### Everything is a String (EIAS)
Tcl's defining principle: **values have no inherent type**. Interpretation is extrinsic—determined by commands, not the value itself.
```tcl
set x 42
expr {$x + 1} ;# Interpreted as integer
string length $x ;# Interpreted as 2-character string
lindex $x 0 ;# Interpreted as single-element list
```
**Key insight**: "8" and the number 8 are the same value. Type is what you *do* with data, not what it *is*.
Internally, Tcl maintains dual representations (string + typed cache) for performance. This is invisible—two identical strings are always equal regardless of internal state.
### Code is Data is Code (Homoiconicity)
Tcl scripts are strings. Strings are data. Therefore code is data, manipulable like any other value.
```tcl
set cmd [list puts "Hello"]
eval $cmd ;# Execute string as code
set body [info body myproc] ;# Introspect procedure body
```
This enables:
- DSL creation via new control structures
- Runtime code generation and modification
- Transparent procedure wrapping (memoization, tracing)
**Anti-pattern**: String concatenation for command building. Use `[list]` to prevent injection and preserve word boundaries.
## Decision Framework: Data Structures
| Need | Use | Why |
|------|-----|-----|
| Sequential data, transformations | **List** | First-class, composable, `lmap`/`foreach` friendly |
| Key-value, pass to procs | **Dict** | First-class value, nestable, preserves order |
| Mutable shared state | **Array** | Never copied, pass by name (`upvar`) |
| Set operations | **List + array index** | `array set` for O(1) membership |
### Lists as Primary Workhorse
Lists handle 80% of collection needs. Prefer flat structures:
```tcl
# Good: flat list, simple operations
set points {10 20 30 40 50 60}
foreach {x y} $points { ... }
# Avoid: premature nesting
set points {{10 20} {30 40} {50 60}}
```
### When Arrays Beat Dicts
Arrays excel for large mutable datasets:
```tcl
# Array: no copy on modification
proc update {arrName key val} {
upvar $arrName arr
set arr($key) $val ;# In-place mutation
}
# Dict: copies on write when shared
proc update {dictVar key val} {
upvar $dictVar d
dict set d $key $val ;# May copy entire dict
}
```
**Rule of thumb**: Dicts for data exchange, arrays for stateful engines.
## Key Patterns
### The K Combinator
Return first argument, discard second. Enables single-expression state changes:
```tcl
proc K {x y} { set x }
# Shift: remove and return first element
proc shift {listVar} {
upvar $listVar lst
K [lindex $lst 0] [set lst [lrange [K $lst [unset lst]] 1 end]]
}
```
### Functional Idioms
```tcl
# Fold (reduce)
proc foldl {func acc lst} {
foreach item $lst { set acc [{*}$func $acc $item] }
set acc
}
# Filter
proc pick {func lst} {
set ret {}
foreach item $lst {
if {[{*}$func $item]} { lappend ret $item }
}
set ret
}
# Use lmap for transforms (Tcl 8.6+)
lmap x $nums { expr {$x * 2} }
```
### Memoization via Proc Wrapping
```tcl
proc memo {procName} {
rename $procName _$procName
proc $procName args [subst -nocommands {
variable _cache_${procName}
if {[info exists _cache_${procName}(\$args)]} {
return [set _cache_${procName}(\$args)]
}
set _cache_${procName}(\$args) [_${procName} {*}\$args]
}]
}
```
### Ensemble Extension
Add subcommands to existing ensembles:
```tcl
proc ::tcl::dict::get? {args} {
try { dict get {*}$args } on error {} { return {} }
}
namespace ensemble configure dict -map \
[dict merge [namespace ensemble configure dict -map] \
{get? ::tcl::dict::get?}]
# Now: dict get? $d key ;# Returns {} on missing key
```
### The Filter Idiom (Suchenwirth)
Unix-style composable scripts:
```tcl
proc filter {chan} {
while {[gets $chan line] >= 0} {
# Process line, puts to stdout
}
}
if {$argc == 0} {
filter stdin
} else {
foreach file $argv {
set f [open $file]
filter $f
close $f
}
}
```
### Iterator Pattern with Coroutines
```tcl
proc iterator {name args body} {
proc $name $args [subst {
yield
$body
return -code break
}]
}
iterator iota {n} {
for {set i 0} {$i < $n} {incr i} { yield $i }
}
coroutine gen iota 5
while {[set v [gen]] ne ""} { puts $v }
```
## Metaprogramming Techniques
### uplevel and upvar
- `upvar`: Create local alias to caller's variable
- `uplevel`: Execute code in caller's scope
```tcl
proc with {varName value body} {
upvar $varName v
set old $v
set v $value
try {
uplevel 1 $body
} finally {
set v $old
}
}
```
### Code Generation
Build commands safely with `list`, execute with `{*}`:
```tcl
# Safe: list preserves word boundaries
set cmd [list exec grep $pattern $file]
{*}$cmd
# Dangerous: string interpolation
eval "exec grep $pattern $file" ;# Injection risk
```
## Anti-Patterns
- **Unbraced expr**: `expr $x + $y` parses at runtime, allows injection. Always `expr {$x + $y}`.
**Exception — Expression-as-String APIs**: Intentionally unbraced `expr` enables flexible APIs where callers pass string expressions:
```tcl
proc iota {fr {to {}}} {
set fr [expr $fr] ;# Unbraced: allows "iota 0 $n+1"
set to [expr $to] ;# Caller's "$n+1" evaluated here
for {set r {}} {$fr < $to} {incr fr} {lappend r $fr}
set r
}
iota 0 $count+1 ;# Works because expr evaluates the string
```
Use this pattern deliberately in utility APIs where flexibility outweighs the (minimal) performance cost. Keep internal arithmetic braced.
- **eval with string building**: Use `{*}[list ...]` or direct invocation instead.
- **Overusing dict/array**: Simple lists suffice for most sequential data. "Even O(N²) may be ok, for sufficiently small N." —RS
- **Deep nesting**: Flat structures are more Tcl-idiomatic. Two lists often beat one nested list.
- **Ignoring dual-porting**: Switching between string and list interpretations causes "shimmering". Keep values in consistent interpretation when performance matters.
## Suchenwirth's Wisdom
- "A good proc fits between thumb and middle finger" — keep procedures concise
- "My spirit is: if a wheel is easier reinvented than reading its documentation, don't use that wheel"
- Tcl's "Play-Doh-like flexibility" permits infix, prefix, and circumfix notations
## References
- [Everything is a String](https://wiki.tcl-lang.org/page/everything+is+a+string)
- [Homoiconic](https://wiki.tcl-lang.org/page/homoiconic)
- [Meta Programming](https://wiki.tcl-lang.org/page/Meta+Programming)
- [Richard Suchenwirth](https://wiki.tcl-lang.org/page/Richard+Suchenwirth)
- [Dict VS Array Speed](https://wiki.tcl-lang.org/page/Dict+VS+Array+Speed)
- [The filter idiom](https://wiki.tcl-lang.org/page/The+filter+idiom)gold 01/20/2026. Added categories, so can find message in Wiki.
| Category Numerical Analysis | Category Toys | Category Calculator | Category Mathematics | Category Example | Toys and Games | Category Games | Category Application | Category GUI |