Search code examples
foreachsettcluplevel

Tcl's foreach not setting variables


I'm redefining Tcl's set command to allow multiple assignments at once. However, once I try to use the variables, it reports an "undefined variable" error. Here are the two ways I've tried so far:

proc set args {
    uplevel foreach {*}$args break
}
proc set {l1 l2} {
    uplevel foreach $l1 $l2 break
}

I performed an llength on $args and it did report a list of two elements. I'm quite confused about this behavior.

I'm calling it like so:

set {a b c d} {e f g h}
set {a s d f} {q w e r t y}

*Edit:

Did a bit more tinkering with it. I found a way to make it work (for the most part, because extra values are silently discarded, but that's irrelevant to the original question), but I'm still confused about WHY this way worked but the others didn't:

proc set {l L} {
    uplevel foreach [list $l] [list $L] break
}

Solution

  • uplevel joins its arguments as if they had been passed to concat, which among other things, says

    If all of the arguments are lists, this has the same effect as concatenating them into a single list.

    So your first version becomes

    foreach a b c d e f g h break
    

    leaving a set to b, c set to d, and so on. Your second version ends up doing the same; the two lists are flattened out and their elements become individual arguments to foreach. The third version, wrapping the arguments in list, becomes your intended

    foreach {a b c d} {e f g h} break
    

    the outer list is stripped, but the inner ones remain.


    As an aside, I would use a different name than set, or define it in a non-global namespace, unless you also intend to also support the single-argument version of set that returns the current value of a variable. Way less confusing that way.