Version 12 of let

Updated 2016-03-20 23:30:56 by LarrySmith
# let.tcl
# Copyright 2001 by Larry Smith
# Wild Open Source, Inc
# For license terms see "COPYING"
#
# let is a replacement for the "set" command.  It allows
# multiple assignment, and supports a variety of assignment
# operators:
#
# let a b c = 1     ;#  this sets a b and c to 1
# let a b c = 1 + 4 ;# "=" uses expr to process the value to assign
# let a b c += 1    ;# computed assignments allowed, +-*/&| supported
# let a b c := info commands ;# uses eval to process value
# let a b c @= 1 2 3;# "hoisting" assignment, foreach replacement
# let a b c @:= info commands;# uses eval and hoists for assignment
# let a ++          ;# incr and
# let a --          ;#   decr are supported.

proc let { args } {
  if { [llength $args ] == 2 } {
    if [string equal [ lindex $args 1 ] "++" ] {
      set result [ uplevel incr [ lindex $args 0 ] ]
    } elseif [string equal [ lindex $args 1 ] "--" ] {
      set result [ uplevel incr [ lindex $args 0 ] -1 ]
    } else {
      set result [ uplevel set "$args" ]
    }
  } else {
    regexp {([^=:+\-*/&|@]*)([:+\-*/&|@]?)([@]*)=(.*)} $args -> vars op optional rest
    if ![ info exists op ] {
      return -code error -errorcode 1 "no valid assignment operator in $args"
    }
    switch -- $op {
      : {
        if [llength [info commands [lindex $rest 0]]] {
          set result [uplevel $rest]
        } else {
          set result $rest                ;# this should always work...
        }
        if { "$optional" == "@" } {
          set max [ llength $result ]
          foreach var $vars res $result {
            uplevel 1 [ list set $var $res ]
          }
        } else {
          foreach var $vars {
            set result [ uplevel set $var \"$result\" ]
          }
        }
      }
      @ {
        if { "$optional" == ":" } {
          set rest [uplevel $rest]
        }
        set max [ llength $rest ]
        if { $max == 1 } {
          eval set rest $rest
          set max [ llength $rest ]
        }
        foreach var $vars res $rest {
          set result [ uplevel 1 [ list set $var $res ]]
        }
      }
      + - - - * - / - & - | {
        foreach var $vars {
          set result [ uplevel set $var \[ expr \$$var $op ( $rest ) \] ]
        }
      }
      = -
      default {
        if { [ catch { set result [ uplevel expr $rest ] } ] } {
          set result $rest              ;# this should always work...
        }
        foreach var $vars {
          set result [ uplevel set $var \"$result\" ]
        }
      }
    }
  }
  return $result
}

DKF - I'd prefer it if stand-alone = was not used, but rather always working with := instead. That's proved to work fairly well. And automagic passing of stuff through eval is not necessary; better to get people to say what they mean IMHO.

Larry Smith The '=' comes from both C and Basic, from which came "let". While a personally agree with you, the vast plethora of C's spawn have placed the equals sign forevermore as the "typical" assignment operator. As for use of eval, the design was intended to remove visual cruft. Making eval explicit just makes it harder to read.


Recently someone on comp.lang.tcl asked about the use of the word hoisting above. What is its meaning in this context?

AMG: Multiple assignments in the style of [foreach] or [lassign]: variables are set to corresponding values, where correspondence is determined by list position. [let a b c @= 1 2 3] would be like [foreach {a b c} {1 2 3} {break}] or [lassign {1 2 3} a b c].


What is the expected output of

  let a b c @:= info commands;# uses eval and hoists for assignment

What I am seeing, with the above code, is that nothing whatsoever is being done.

PYK 2016-03-19: Can confirm. I attempted to fix this bug today, but after working with it for a bit, decided that this code has too many issues to salvage. The alleged functionality has been largely implemented in lassign these days anyway. I move to delete this entire page on 2016-03-31.

HE 2016-03-20: Hello PYK no one asked you to fix the code. So, instead to delete the side you should consider to explain other readers what is wrong with this code. In particular because lassign could only replace one of the different ways you can call the code above:

Larry Smith "Let" was suggested before lassign was added to the language. As for "bugs" and "not working", using exactly the above code I executed

 let a b c @= {1 2 3}
 puts "a=$a, b=$b c=$c"

and got exactly what I advertised. If you don't find it intuitive, that's your taste, but that doesn't mean it's broken.

 $ tcl temp.tcl
 a=1, b=2 c=3
set l [list 1 2 3]
let a b c @= $l
puts "$a $b $c"

is simimlar to

set l [list 1 2 3]
lassign $l a b c
puts "$a $b $c"

But in case someone wants a single value assign to more than one variable

let a b c := [expr {4 * 4 * 5}]
puts "$a $b $c"

you have to use a help variable. For sure the expression could be more complex for example relay on other variables.

set tmp [expr {4 * 4 * 5}]
lassign [list $tmp $tmp $tmp] a b c
puts "$a $b $c"

or perhaps something like

set a [set b [set c [expr {4 * 4 * 5}]]]

I can see that the code has some quirks and perhaps errors, too. But why deleting the page?


See also