Ralf
Ralf

Reputation: 1295

tcl: proc body in quotes instead of curly brackets

Is there any problem (e.g. a performance penalty) if the body of a proc is provided in quotes rather than curly brackets?

My code generates procs (as OOP-like methods) inside of other procs, e.g.

proc dataObject {name someData} {
  # more stuff
  proc ${name}.getData {args} \
    "checkArgs \$args 0; return $someData"
  # more stuff
}

and for simplity, I use quotes to enable variable substitution. It works, but I'm just worried that the code may not be precompiled or something.

Thanks for your help!

Upvotes: 1

Views: 176

Answers (2)

Donal Fellows
Donal Fellows

Reputation: 137567

It is difficult to do completely reliable code generation, but not impossible and using double quotes around a procedure body is entirely legal. I advise constraining the space of inserted words to non-empty alphanumeric prior to doing the codegen; that stops almost all mischief dead; other values need to be quoted (the list command can do exactly the right quoting you need with a little encouragement). It's often easier to generate an alias that curries some extra arguments onto a call of a non-varying procedure. Here's a very simple example of what I mean:

proc saySomething {a b} {
    puts -nonewline $a
    puts $b
}

proc makeSpeaker {cmd prefix} {
    interp alias {} $cmd {} saySomething "[string trimright $prefix] "
}

makeSpeaker hello "Hello to"
hello Ralf
# ==> Hello to Ralf

As you can see, we've “generated” code that includes a word with a space in it without having to do complex quoting. It can't do everything, but it can do a lot.

And don't make your own pseudo-OO code. Not these days. Tcl from 8.6 onwards comes with an OO system core that makes doing that stuff much faster and more reliable.

oo::class create Speaker {
    variable Prefix
    constructor {prefix} {
        set Prefix "[string trimright $prefix] "
    }
    method say {suffix} {
        puts -nonewline $Prefix
        puts $suffix
    }
}

Speaker create greeting "Hello to"
greeting say Ralf

Of course, you can mix these two together to get some truly powerful approaches, but then the example's getting a bit long for quick comprehension...

Upvotes: 1

mrcalvin
mrcalvin

Reputation: 3434

I'm just worried that the code may not be precompiled or something.

This is not to worry about, besides, there is no precompilation or similar in Tcl. Once executed for the first time, the generated proc's body will be byte-compiled (however the body script was assembled).

However, your proc generator is not robust. Variable substitution under quotes will break your body script when someData contains a string which renders the body script or one of its commands incomplete, e.g.:

dataObject test "do it"

will fail because it translates into

return do it;

There are several ways to assemble a script (command-sequence string) in a robust manner, one is using list protection:

proc dataObject {name someData} {
    set procName ${name}.getData
    append body {checkArgs $args 0} \;
    append body [list return $someData] \;
    proc $procName {args} $body
    return [namespace which -command $procName]
}

As pointed out by Donal in another answer, nesting proc calls one in another is not necessarily leading to what you expect. In your case, though, as a generator, it might be acceptable. Still, you might want consider using a Tcl lambda or a proper (well, data) object?

Upvotes: 3

Related Questions