Version 66 of interp alias

Updated 2019-12-03 14:33:26 by GlingON

interp alias creates and manages aliases, both within an interpreter (great for shortcuts) and between interpreters (vital for safe tcl).

Synopsis

interp alias srcPath srcToken

Returns a list whose elements are the targetCmd and args associated with the alias token named srcToken, which is not the name of a command in srcPath, but the token that was returned when the alias was created. It is not guaranteed that a token for an alias is the name of a command that is an alias. For one thing, the alias may have been renamed since it was created.

interp alias srcPath srcToken {}

Deletes the alias for srcToken in the slave interpreter identified by srcPath. srcToken refers to the name under which the alias was created; if the source command has been renamed, the renamed command will be deleted.

interp alias srcPath srcCmd targetPath targetCmd ?arg arg ...?

Creates an alias between one slave and another (see the alias slave command below for creating aliases between a slave and its master), and returns a token for that alias. In this command, either of the slave interpreters may be anywhere in the hierarchy of interpreters under the interpreter invoking the command. SrcPath and srcCmd identify the source of the alias. SrcPath is a Tcl list whose elements select a particular interpreter. For example, "a b" identifies an interpreter b, which is a slave of interpreter a, which is a slave of the invoking interpreter. An empty list specifies the interpreter invoking the command. srcCmd gives the name of a new command, which will be created in the source interpreter. TargetPath and targetCmd specify a target interpreter and command, and the arg arguments, if any, specify additional arguments to targetCmd which are prepended to any arguments specified in the invocation of srcCmd. TargetCmd may be undefined at the time of this call, or it may already exist; it is not created by this command. The alias arranges for the given target command to be invoked in the target interpreter whenever the given source command is invoked in the source interpreter.

interp aliases ?path?

Returns a list of tokens for all the aliases in path. If path is not provided, the current interpreter is used.

See Also

alias
Custom curry
DKF explained, "What aliases do, apart from moving between interps, is let you convert [a b c] into [P Q R b c]".
interp
interp aliases
Introspection on aliases
proc alias
make an alias to a proc without the potential performance cost of [interp alias]
proc or interp alias
proc-local alias
Transliteration
how to use an alias, besides its original function, as a kind of global data storage
custom curry
how currying in other languages is analgous to some uses of [interp alias]
Rohan Pall
presents a clever use of aliases on his home Wiki page

Description

Simple Command Aliases

Very often used to introduce command aliases inside the current interpreter (see Custom curry), where the alias target may well be a sequence of words:

interp alias {} strlen {} string length

The case where source and target have identical signatures is Tcl's idiomatic way to write a command alias:

interp alias {} new_name {} existing_command

This can be used with some punctuation to make code shorter and potentially more readable as long as the reader knows about the alias(es) in effect.

interp alias {} @ {} lindex

To delete such an alias again, just do:

interp alias {} @ {}

Cleverness due to dgp (and others):

package require Tcl 8.5
interp alias {} err {} return -code error -level 0

interp alias {} = {} expr
interp alias {} -> {} set res
-> [= $x+1]

so assignment to a default variable can be sugared (see Euro converter for an application) RS. This tastes like sweet curry:

proc curry {new old} {eval [list interp alias {} $new {}] $old}
curry -> {set res}

Or the modern eval-less version GlingON:

proc curry {new old} {interp alias {} $new {} {*}$old}

An example of very practical use, when interactively debugging:

interp alias {} ? {} set errorInfo

Then a simple question mark shows you the full error stack. (RS)

Be aware that interp alias hides existing commands without complaining, so better check with info commands before that the name isn't taken yet.


RS:

Sugar to avoid the word eval:

% interp alias {} foo: foo eval
foo:
% foo: set bar 1
1
% foo: set bar
1
% foo: proc bar {x} {string toupper $x}
wrong # args: should be "proc name args body"

Oops - it still is an eval, so needs one list layer more:

% foo: {proc bar {x} {string toupper $x}}
% foo: bar hello
HELLO

LES: This little thing puzzles me:

interp alias {} exists {} file exists

Then exists hi.txt returns 1. The file is there.

But file exists hi.txt also returns 1. How does it avoid looping forever into file exists = file file exists = file file file exists = file file file file exists ...

schlenk: Because an alias is a command and this is only significant in the first word. The command for file is just 'file' and its first argument is 'exists'. So there is no loop. (But interp alias has loop detection built in...)

Object-Oriented Dispatch

RS: interp alias is a big sugar provider for doing OO - to be able to call a method in the usual

$object method arg...

way, it's sufficient to alias that to a dispatcher (with the "self" pointer as first argument):

interp alias {} $object {} ${class}::dispatch $object

Misc

SYStems: Okay, since the example below from Brent Welch's book doesn't loop forever, we can assume the alias open doesn't shadow the hidden command open; where is this documented? I think I searched everywhere, plus I played around this a little, and this seem kind of inconsistant, as in how come slave interp still thinks open is hidden, when it gets an exposed alias called open. The hidden command open should be renamed first, then replaced by the alias open; the example below should have raised the error invalid hidden command name "open", since slave now have an alias called open; open should have been remove from its list of interp hidden, right or what?

# Example 19-6
# Substitutions and hidden commands.
#
interp alias slave open {} safeopen slave

proc safeopen {slave filename {mode r}} {
    # do some checks,then...
    interp invokehidden $slave open $filename $mode
}
interp eval slave {open \[exit\]}

RS: open for the slave is safeopen in the master. This way, the master's open is not lost.


jcw: The following might be surprising:

% interp alias {} pass {} return
pass
% pass 1
1
% proc a {b} { pass $b; return 2 }
% a 3
3
%

Explanation: an alias is not the same thing as wrapping a call in a proc.

Here's a question: is there an alias which does act as a no-op? I'm looking for something like:

% interp alias {} pass {} ...?
pass
% pass 1
1
% proc a {b} { pass $b; return 2 }
% a 3
2
%

Extra requirement: pass must return its argument. Only trick I can think of is with a dummy variable:

interp alias {} pass {} set a-name-which-is-never-used-anywhere

MM: This may work:

interp alias {} pass {} format %s

but one-and-only-one argument is mandatory.

MG: My understanding of interp alias is that it just creates a 'pointer' to another command, and when the alias is called it runs the command it 'points' to instead, with the same args. So what you need is a command which takes a string (as its last argument), and returns that string unchanged, without having any side effects (such as return's side effect of stopping execution of the current proc, etc). Apart from

format %s

mentioned above, you could also use

interp alias {} pass {} subst -nocommand -novariable -nobackslash

but that also only takes one arg. A few very basic tests of

interp alias {} pass {} concat

seem to work with multiple args, though you have to properly protect them by quoting, or the concat affects list-nesting (which has an effect on strings containing brace characters), which isn't desirable at all. The easiest way by far, though, is to not use interp alias, but a proc:

proc pass {args} {return $args}

which you can then alias later, if you want to be able to call it by multiple names - but then, where's the fun in that? :) I expect RS or someone else will come up with many different ways that I've not thought of. :)

jcw: Thanks. Single arg suits me just fine. I'm looking for the fastest no-op (have not done any timing measurements yet). One which can be replaced later in the app when needed, but which has minimal impact when left as is. The "format %s" trick is nice, but has as drawback that it loses the dual-object representation of its argument. Yes, proc pass is an option, clearly.

DKF: In 8.5, the best solution is:

interp alias {} pass {} return -level 0

RS proposes this (can do multiple args, does not change internal rep):

% interp alias {} pass {} list
pass
% pass 1
1
% pass 2 3 4
2 3 4

MG: One thing to beware of, with both the proc version above and RS's alias of list, is that (when doing multiple args), the quoting is likely to be altered. For instance (using either the proc/list methods):

% pass this is "a test"
this is {a test}

With the concat alias, it produces

% pass this is "a test"
this is a test

because it concats the lists - but with that, at least, you can use

% pass this is {"a test"}
this is "a test"

They still don't quite work correctly with multiple args, as they are, though.


jk is always tempted by things (which don't work) like:

interp alias {} glob {} glob -nocomplain

obviously you can get it to work if you change the name of the source or rename the target, but this is somehow unsatisfying

Lars H: In that case, try using a second interpreter

interp create helper
interp alias {} glob helper glob -nocomplain

Or even sillier, interp hide the normal glob command:

interp hide {} glob
interp alias {} glob {} interp invokehidden {} -- glob -nocomplain

jk: yes using hide is analogous to rename (although, maybe a little clearer as to what you are doing) this seems to have the advantage over the helper interp if you want to be able to remove the alias again however