<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE TIP SYSTEM "http://www.tcl.tk/cgi-bin/tct/tip/tipxml.dtd">
<!-- Converted at Thu May 17 03:13:54 GMT 2012 -->
<!-- TIP AutoGenerator - written by Donal K. Fellows -->

<TIP number='181'>
<header><title>Add a [namespace unknown] Command</title><author address="mailto:nem@cs.nott.ac.uk">Neil Madden</author><status type='project' state='final' tclversion="8.5" vote='after'>$Revision: 1.33 $</status><history></history><created day='23' month='mar' year='2004' /></header>
<abstract>This TIP proposing adding a new <emph style="bold">namespace</emph> subcommand, <emph style="bold">unknown</emph>, which would register a per-namespace procedure for dealing with unknown commands.</abstract>
<body><section title="Rationale">
<para>There is an occassional need within Tcl scripts to change the way in which command names are resolved, for instance when implementing language constructs such as object systems or lexically-scoped commands. Many of these issues are addressed by the newly-accepted <emph style="bold">namespace path</emph> command <tipref type="text" tip="229"/>. However, there are still a few situations where more customized behaviour is required. For instance, implementing custom per-namespace command auto-loading, or to use auto-expansion of leading word as in TOOT [<url ref="http://wiki.tcl.tk/11543"/>]. Currently, the only way to perform such customized command resolution behaviour is to override the global <emph style="bold">::unknown</emph> proc to install your custom handler. There are several drawbacks with this mechanism.</para>
<para>Firstly, it is difficult to override the global <emph style="bold">::unknown</emph> proc if you are writing a package, as good style dictates that you shouldn&apos;t override commands outside your package namespace without being explicitly asked to do so.</para>
<para>Secondly, as Tcl searches for a hard-coded fallback procedure name (<emph style="bold">::unknown</emph>), in order to override it&apos;s functionality you have to rename it and then install your own replacement - and the new version becomes the default fallback behaviour for the entire application. In the case of implementing custom auto-loading behaviour, you may only want to override the behaviour for your namespace, and not for the entire interpreter. Currently, the only way to do this is to define a new <emph style="bold">::unknown</emph> procedure which does pattern matching on the command name it is passed.</para>
<para>Finally, if a package does override the <emph style="bold">::unknown</emph> procedure it has to be careful to save the old handler, and then invoke it for commands which it is not interested in. This is an error-prone approach, and results in a cascade of procedure calls, often with each one only interested in a subset of the commands being searched for.</para>
</section>
<section title="Related TIPs">
<para>There have been two previous attempts at modifying Tcl&apos;s command resolution process. <tipref type="text" tip="52"/> proposed that the search order be changed to traverse the complete namespace hierachy from most specific namespace to the most general (the global namespace). This TIP was withdrawn as it was not backwards compatible. <tipref type="text" tip="142"/> proposed a global variable which would hold a namespace search path. This TIP was also withdrawn as it does not allow different namespaces to have different search paths. <tipref type="text" tip="229"/> proposed the <emph style="bold">namespace path</emph> command as a way of specifying a per-namespace search path for command resolution. This TIP was accepted, and provides a good general mechanism for custom command resolution. The current TIP is complementary to <tipref type="text" tip="229"/> and can be used to handle cases where more flexible behaviour is required.</para>
</section>
<section title="Proposed Change">
<para>This TIP proposes that the handling of unknown commands be done on a per-namespace basis through the introduction of an <emph style="bold">unknown</emph> subcommand of the <emph style="bold">namespace</emph> command.</para>
<quote><emph style="bold">namespace unknown</emph> ?<emph style="italic">commandPrefix</emph>?</quote>
<para>The subcommand would accept either zero or one argument(s), and is similar in interface to the <emph style="bold">interp bgerror</emph> command added by <tipref type="text" tip="221"/>. If no arguments are given, the command returns the handler for the current namespace. The optional argument <emph style="italic">commandPrefix</emph> is a command (strictly a prefix list consisting of a command and optional arguments) to execute if normal command lookup from the current namespace fails. The command will be concatenated with the full invocation line of the command being searched for (i.e. the command name and all arguments), and evaluated in the scope of the current namespace. The first word in the list given must be a command name which must be able to be resolved without resorting to the unknown mechanism (i.e. it must either be a command in the current or global namespace, or be fully-qualified). If this cannot be done, a stock error message will be generated referring to the original unknown command (and <emph style="italic">not</emph> the missing handler) - this is how Tcl currently behaves if no <emph style="bold">::unknown</emph> procedure exists.</para>
<para>The command resolution procedure would be altered from this:</para>
<enumerate><item.e index='1'><para>Lookup command in current namespace.</para></item.e><item.e index='2'><para>If that fails, use path supplied by <emph style="bold">namespace path</emph>.</para></item.e><item.e index='3'><para>If that fails, lookup command in global namespace.</para></item.e><item.e index='4'><para>If that fails, call global <emph style="bold">::unknown</emph> procedure.</para></item.e></enumerate>
<para>to this:</para>
<enumerate><item.e index='1'><para>Lookup command in current namespace.</para></item.e><item.e index='2'><para>If that fails, use path supplied by <emph style="bold">namespace path</emph>.</para></item.e><item.e index='3'><para>If that fails, lookup command in global namespace.</para></item.e><item.e index='4'><para>If that fails, call the unknown handler for the namespace in which the unknown command was invoked.</para></item.e></enumerate>
<para>Note that this TIP does not change (or allow changing) the default command resolution procedure - the current, namespace path, and global namespaces are always searched before the unknown handler is called. This is so that resolution of the unknown handler itself can be performed, and so that the handler can be implemented without resorting to fully qualifying every command in it (e.g. having to use ::set).</para>
<para>It also should be noted that the unknown handler that is called is for the namespace that <emph style="italic">invoked</emph> the unknown command, rather than the namespace which is the <emph style="italic">target</emph> of the call. There are a number of reasons for this decision: Firstly, there is no guarantee that the target namespace actually exists (this might be a reason why the command was not found). In that case, there would be no target namespace unknown handler, and we would be forced to fallback on the global default. Secondly, the mechanism is designed mainly for namespace authors who wish to implement some custom behaviour that affects the operation of their own code, e.g., custom auto-loading of missing commands, or more sophisticated command lookup procedures, etc. This suggests that the responsibility for dealing with unknown commands should fall on the originating namespace, rather than being placed on arbitrary other namespaces. It is believed that this is the most sensible, and the most predictable, behaviour for an unknown command mechanism. In addition, with the introduction of <emph style="bold">namespace ensemble</emph> <tipref type="text" tip="112"/> there now exists a flexible mechanism for handling the opposite behaviour, allowing the target namespace to handle requests for unknown commands (e.g., forwarding requests). The author of the present TIP considers this to be the right division of responsibility, as ensembles should become the default mechanism for accessing public operations of external namespaces, and also an ensemble command can be guaranteed that the target namespace does indeed exist.</para>
<para>The default unknown handler for the global namespace is a handler called <emph style="bold">::unknown</emph>. The default handler for other namespaces calls the global unknown handler. This means that by default, we have exactly the same mechanism that exists currently in Tcl. In order to change the mechanism for an individual namespace, you may register a new unknown handler for that namespace. When no handler is registered for a namespace, then a call to <emph style="bold">namespace unknown</emph> will return an empty string (for non-global namespaces) or <emph style="bold">::unknown</emph> for the global namespace. This is so that a distinction can be made between namespaces which have no handler set, and namespaces which have had an unknown handler called <emph style="bold">::unknown</emph> deliberately registered for them. With this scheme it is possible to set a global per-interpreter unknown command handler by setting the unknown handler for the global namespace. This can then be overridden on a per-namespace basis, if required.</para>
<para>The calling of unknown handlers registered with <emph style="bold">namespace unknown</emph> would be identical to the current calling of the <emph style="bold">::unknown</emph> procedure - the handler will be called with the command name and all of its arguments, as it was originally invoked. The handler should be a valid Tcl list representing a command and possible initial arguments. For instance, a single handler proc could be used for several namespaces with the namespace passed as an argument:</para>
<verbatim><vline encoding='base64'>IHByb2MgaGFuZGxldW5rbm93biB7bnMgY21kIGFyZ3N9IHsgLi4uIH0=</vline><vline encoding='base64'>IG5hbWVzcGFjZSBldmFsIGZvbyB7IG5hbWVzcGFjZSB1bmtub3duIFtsaXN0IDo6aGFuZGxldW5rbm93biA6OmZvb10gfQ==</vline><vline encoding='base64'>IG5hbWVzcGFjZSBldmFsIGJhciB7IG5hbWVzcGFjZSB1bmtub3duIFtsaXN0IDo6aGFuZGxldW5rbm93biA6OmJhcl0gfQ==</vline></verbatim>
<para>Setting the unknown handler to {} (an empty list) restores the default handler (<emph style="bold">::unknown</emph> for global namespace, global unknown handler for all other namespaces).</para>
<subsection title="C API">
<para>Additionally, this TIP proposes adding two new public functions to the Tcl C-API to expose this functionality at the C-level. The proposed new functions are:</para>
<verbatim><vline encoding='base64'>IFRjbF9PYmogKlRjbF9HZXROYW1lc3BhY2VVbmtub3duSGFuZGxlcihUY2xfSW50ZXJwICppbnRlcnAs</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUY2xfTmFtZXNwYWNlICpuc1B0cik7</vline></verbatim>
<para>Returns the current unknown command handler registered for the given namespace, or NULL if none is.</para>
<verbatim><vline encoding='base64'>IGludCBUY2xfU2V0TmFtZXNwYWNlVW5rbm93bkhhbmRsZXIoVGNsX0ludGVycCAqaW50ZXJwLA==</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGNsX05hbWVzcGFjZSAqbnNQdHIs</vline><vline encoding='base64'>ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGNsX09iaiAqaGFuZGxlclB0cik7</vline></verbatim>
<para>Sets the unknown command handler for the namespace, or resets it to the default if <emph style="italic">handlerPtr</emph> is NULL or an empty list.</para>
</subsection>
<subsection title="Consequences">
<para>As a final note, there is a useful side-effect to always resolving the unknown handler itself in the current namespace, in that an unknown handler can be registered for the global namespace which is not fully qualified, and it will be resolved relative to the namespace in which an unknown command is invoked. To illustrate:</para>
<verbatim><vline encoding='base64'>ICMgU2V0IGdsb2JhbCB1bmtub3duIGhhbmRsZXIgdG8gdW5xdWFsaWZpZWQgbmFtZQ==</vline><vline encoding='base64'>IG5hbWVzcGFjZSB1bmtub3duIHVua25vd24=</vline><vline encoding='base64'>IG5hbWVzcGFjZSBldmFsIGZvbyB7IHByb2MgdW5rbm93biB7YXJnc30geyBwdXRzICJGT08iIH0gfQ==</vline><vline encoding='base64'>IHByb2MgdW5rbm93biB7YXJnc30geyBwdXRzICJHTE9CQUwiIH0=</vline><vline encoding='base64'>IA==</vline><vline encoding='base64'>IGJhciA7IyBwcmludHMgR0xPQkFM</vline><vline encoding='base64'>IG5hbWVzcGFjZSBldmFsIGZvbyB7IGJhciB9IDsjIHByaW50cyBGT08=</vline><vline encoding='base64'>IG5hbWVzcGFjZSBldmFsIG90aGVyIHsgYmFyIH0gOyMgcHJpbnRzIEdMT0JBTA==</vline></verbatim>
</subsection>
</section>
<section title="Reference Implementation">
<para>A reference implementation is available attached to Patch 958222 on the Tcl project at SourceForge [<url ref="http://sf.net/tracker/?func=detail&amp;aid=958222&amp;group_id=10894&amp;atid=310894"/>].</para>
<para>The current patch is called tip181-4.patch</para>
</section>
<section title="Copyright">
<para>This document has been placed in the public domain.</para>
</section>
</body></TIP>

