shorthand dict set

Syntax sugar for "dict set" and "dict get".

rename set tcl_set

proc set {var_name args} {
    # usage: set var_name value
    #    or: set dict_name key... value

    upvar $var_name var
    if {[llength $args] < 2} {
        tcl_set var {*}$args
    } else {
        dict set var {*}$args
    }
}


proc get {args} {
    # usage: get dict key...
    #   get: get value

    if {[llength $args] == 1} {
        lindex $args 0
    } else {
        try {
            dict get {*}$args
        } trap {TCL LOOKUP DICT} {} {}
    }
}


proc exists {args} {
    # usage: exists var_name
    #    or: exists dict key...

    if {[llength $args] == 1} {
        uplevel [list info exists {*}$args]
    } else {
        uplevel [list dict exists {*}$args]
    }
}

This over-loads plain-old "set" to allow "set dict_var key... $value" as shorthand for "dict set dict_var key... $value". Normal "set" behaviour is retained.

"get" is there too for symmetry, but silently returns {} for missing keys.

Some may see this as heresy, but I think it makes dictionary-heavy scripts more readable :) See https://github.com/samoconnor/oclib.tcl/blob/master/oclib/oc_base-1.0.tm


AMG: Avoiding [uplevel] which is slightly more dangerous than [eval]:

rename set _set
proc set {varName args} {
    upvar 1 $varName var
    if {[llength $args] <= 1} {
        _set var {*}$args
    } else {
        dict set var {*}$args
    }
}
interp alias {} get {} dict get

Also, because no caller variables are being accessed, there's no reason to use [uplevel] in your [get] command. I used [interp alias] to create an alias, since renaming (actually, partial application of command prefixes) is all that's happening. It's also possible to write: proc get {dict args} {dict get $dict {*}$args}.

One problem I have with this code is that the error message always complains about "var", never the original variable name. Sorry about that.

A more fundamental and philosophical problem is the asymmetry between variable [set] and dictionary [dict set]. Variable [set] can be used to read a value without changing it, but dictionary [dict set] must always set the value.


samoc: Interp alias is a neat trick. But I like proc get {dict args} {dict get $dict {*}$args} better on the KISS principle. Thanks for that. Why use stack twiddling or interpreter magic when a simple function operating on values will do!

I'm interested by "uplevel is dangerous"... I know it's easy to get the list-ificaiton and multiple levels of substitution right. But, in the case of "proc set" above, do you see an actual dangerous corner case?

AMG: [uplevel] has all the same problems as [eval], plus it runs the code with a different set of local variables. Obviously that's what you want, or else you wouldn't use it. But the trouble is that it's coarse-grained and can have effects beyond your intention. For instance, you might leave behind temporary variables in your caller's stack, or worse still, your temporaries can overwrite the caller's like-named variables. Normally I suggest using [upvar] so only the variables you really intend to access are made accessible. -- samoc: upvar is faster too

samoc: With regard to the asymmetry between "set" and "dict set". Yes :) ... That's what you get with Tcl. Can't change the history. But Tcl is a pragmatist's language, and ignoring/accepting this asymmetry works for me.

I have, on numerous occasions, gone off and learned other languages that are: new-fangled, pure-from-the-ground-up, much-bettery-than-all-the-old ones etc... perl, java, eiffel, lua, python, javascript... However, they all have their own rough edges and annoying idiosyncrasies. I keep coming back to Tcl.

My favourite thing about Tcl is that it slowly absorbs neat features from more recent languages and frameworks but it doesn't do it in a big rush. Mostly this leads to a neat minimal implementation.

My current itch is that there are a few languages I've used (e.g. Objective-C and javascript) where a dictionary is and object is a dictionary. This feels right. I've written a lot of Tcl code that deals with (sometimes deeply nested) dictionaries, but I find myself wishing for a dot-notation type syntax person.address.street.number without needing to say "dict get" and "dict set" all the time. I considered implementing some kind of "unknown" magic monster, but "proc set" above seemed lighter and more Tcl-ish.

Here is another thing I find myself doing a lot to create a literal dictionary whose values are not static:

set dict [subst {
    key1 $value1
    key2 $value2
    key3 {
         a [load $a]
         b [load $b]
    }
}]

"dict create" seems kind of clumsy because newline escapes are needed at the end of each line if you want to wrap and align things neatly. The above looks cleaner to me. It has the downside that the values must expand to proper list elements, but this cab be handled by doing [list $value] where needed.

PYK 2014-05-20: Scripted List is fun sugar for this.

AMG: You might want to look at Brush sometime. It does a great deal to tidy up the dict situation. The above can be written:

set &dict (
    key1 $value1
    key2 $value2
    key3 (
        a [load $a]
        b [load $b]
    )
)

Then it can be accessed like so:

puts $dict
puts $dict(key1)
puts $dict(key2)
puts $dict(key3)
puts $dict(key3 a)
set &dict(key3 c) [load $c]
unset &dict(key3)
set &dict(key4) (d [load $d] e [load $e])
unset &dict(key4 e)
set &path (key4 d)
puts &dict({*}$path)

I don't want to say too much about Brush outside its wiki page, since it is not Tcl, but I think this little bit is appropriate here because this page is already discussing alternative syntax for dict access.