Skip to content
VLSI Mentor

Tcl Part I · Module 10 · Procedures and Reuse

Default Arguments

A parameter can carry a value to use when the caller does not supply one, which lets a procedure grow new options without breaking every existing call. The syntax is one small change to the parameter list. The behaviour around it is worth more attention than the syntax, in two places: a default is stored as literal text rather than evaluated, which surprises anyone who tries to default a parameter to the current time; and Tcl will happily let you write a parameter list in which a default can never be used, without a word of complaint. Both are demonstrated here rather than asserted.

Foundation11 min readTclPart IProceduresDefaultsArguments

Part I · Module 10 · Chapter 10.3 · Default Arguments

1. The syntax

Replace a parameter name with a two-element list: the name and its default.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc retry {command {attempts 3}} {
    return "running $command, up to $attempts time(s)"
}

puts [retry build]
puts [retry build 5]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
running build, up to 3 time(s)
running build, up to 5 time(s)

command is required; attempts is optional and falls back to 3.

2. It shows up in the usage line

From Chapter 10.2 §4, the generated message marks optional parameters with question marks:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
retry
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wrong # args: should be "retry command ?attempts?"

So a caller who gets it wrong is told immediately which arguments they may omit.

3. Several defaults

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc report {title {width 40} {border "="}} {
    return "[string repeat $border $width]\n$title"
}

puts [report "One"]
puts [report "Two" 10]
puts [report "Three" 10 "-"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
========================================
One
==========
Two
----------
Three

They fill from the left: supply the first optional parameter to reach the second. There is no way to skip one — Tcl matches by position only, so report "x" "" "-" would set width to the empty string rather than leaving it at 40.

4. Defaults can be any value

Including the empty string, and values containing spaces when grouped:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc label {text {prefix ""} {suffix " <--"}} {
    return "$prefix$text$suffix"
}

puts [label "hello"]
puts [label "hello" ">> "]
puts [label "hello" ">> " "!"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
hello <--
>> hello <--
>> hello!

An empty default is the usual way to say "optional, and absent by default" — then test it with eq "" as Chapter 8.7 described.

5. Tcl does not check that defaults come last

Here is the behaviour worth knowing, and it is not what most tutorials say.

You might expect Tcl to reject a parameter list where a required parameter follows an optional one. It does not:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc weird {{a 1} b} {
    return "a=$a b=$b"
}
puts "defined without complaint"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
defined without complaint

No error. Now try to use the default:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
weird 9
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wrong # args: should be "weird ?a? b"

That call failed. Arguments bind by position, so a single argument goes to a — and then b has nothing, which is an error. The only way to satisfy b is to supply both:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts [weird 9 8]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a=9 b=8

So a's default can never be used. The parameter list is legal, the procedure works, and the default is dead code — while the usage line cheerfully advertises ?a? as optional.

6. A default is text, not an expression

The second surprise, and it arrives in two stages.

First, a bracketed default has to be grouped, because the parameter specifier is itself a two-element list. Written bare, it is not one:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc stamped {msg {when [clock format 0 -format %Y]}} { }
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
too many fields in argument specifier "when [clock format 0 -format %Y]"

Tcl counted six words where it wanted two. Quote or brace it and the definition succeeds — which is where the real surprise is, because the default is then stored as a literal and used as-is. It is not evaluated when the procedure is called:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc stamped {msg {when "[clock format 0 -format %Y]"}} {
    return "$when: $msg"
}
puts [stamped "hello"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[clock format 0 -format %Y]: hello

The brackets were never run. The default is the literal 31 characters, and that is what landed in when.

To compute a default, use an empty default and fill it in the body:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc stamped2 {msg {when ""}} {
    if {$when eq ""} {
        set when [clock format 0 -format %Y]
    }
    return "$when: $msg"
}
puts [stamped2 "hello"]
puts [stamped2 "hello" "1999"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1970: hello
1999: hello

This is the standard shape for any default that has to be worked out rather than written down — a timestamp, a computed path, a value read from configuration.

7. Defaults and args together

args (Chapter 10.4) can follow defaulted parameters:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc both {a {b 2} args} {
    return "a=$a b=$b args='$args'"
}

puts [both 1]
puts [both 1 9]
puts [both 1 9 x y]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a=1 b=2 args=''
a=1 b=9 args=''
a=1 b=9 args='x y'

The binding order is visible across the three calls: positional arguments fill a first, then b, and only what is left over goes to args. A second argument can never reach args while b is still unfilled.

8. A worked example

Growing a procedure without breaking its callers:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# version 1 — one argument
proc summarise {rows} {
    return "[expr {[llength $rows] / 2}] entries"
}
puts [summarise {a 1 b 2}]

# version 2 — a new option, every old call still works
proc summarise {rows {prefix "summary: "} {empty "nothing to report"}} {
    if {[llength $rows] == 0} { return $empty }
    return "$prefix[expr {[llength $rows] / 2}] entries"
}

puts [summarise {a 1 b 2}]
puts [summarise {a 1 b 2} ">> "]
puts [summarise {}]
puts [summarise {} ">> " "(empty)"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 entries
summary: 2 entries
>> 2 entries
nothing to report
(empty)

The original call still works unchanged, which is the whole point of defaults: they let an interface grow without a flag day.

9. Exercises

Exercise 1. Write greet taking a name and an optional greeting defaulting to hello.

Exercise 2. What usage line does proc f {a {b 1} {c 2}} produce when called with no arguments?

Exercise 3. Why can a's default never be used in proc weird {{a 1} b}, and does Tcl warn you?

Exercise 4. One of these fails to define and the other prints something unexpected. Say which is which, and what the author intended by both.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc f1 {{n [expr 2+2]}}   { return $n }
proc f2 {{n "[expr 2+2]"}} { return $n }

Exercise 5. Rewrite the procedure from Exercise 4 so the default is actually computed.

Exercise 6. A procedure has four optional parameters and callers keep writing f $x {} {} "value". What does that indicate, and what would you change?

Answers

1.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc greet {name {greeting hello}} {
    return "$greeting, $name"
}
puts [greet world]
puts [greet world "good morning"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
hello, world
good morning, world

2. wrong # args: should be "f a ?b? ?c?" — one required parameter and two optional ones.

3. Because arguments bind by position: one argument fills a, leaving b unsatisfied, so any call that omits a fails. The only valid calls supply both, and then a's default is never consulted. Tcl does not warn — it accepts the definition silently and the usage line even shows ?a? as though it were optional.

4. f1 does not define at all — the specifier n [expr 2+2] is four words, not two:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
too many fields in argument specifier "n [expr 2+2]"

f2 defines, because the quotes make the default a single element, and then prints the literal ten characters [expr 2+2]. The author intended 4 in both cases. Grouping fixes the list, and nothing fixes the evaluation — defaults are never evaluated.

5.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc f {{n ""}} {
    if {$n eq ""} { set n [expr {2 + 2}] }
    return $n
}
puts [f]
puts [f 9]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
4
9

6. It indicates the parameters are being used as named options, which positional arguments cannot express — the empty strings are placeholders to reach a later one. Replace them with a single options dictionary the caller fills in by name, or reorder so the most-overridden parameter comes first.

10. Remember

  • {name default} in the parameter list makes a parameter optional.
  • Optional parameters show as ?name? in the generated usage line.
  • They fill from the left — there is no way to skip one, so order them by how often they are overridden.
  • Tcl does not check that defaults come last. A required parameter after an optional one is accepted silently and makes the default unreachable, while the usage line still claims it is optional.
  • A default is stored as literal text and never evaluated — {when [clock seconds]} stores the brackets, not the time.
  • To compute a default, use "" and fill it in the body with an eq "" test.
  • args may follow defaulted parameters; positional arguments fill the named parameters first and the rest go to args.
  • Defaults let an interface grow without breaking existing callers.

Where this fits

Part of the Tcl curriculum.