Namespace

Namespaceは、パッケージ同士のシンボル(変数とプロシジャ)の衝突を避けるための機能です。
オブジェクト指向風に変数とプロシジャを結び付けるのに使えて、クラスのようにも見えますが、
クラスほどの機能はありません。本格的にクラスを使うには、[incr Tcl]拡張を使う必要があります。

それでは、namespaceの簡単な例を説明します。

変数とプロシジャ

以下はHelloという名前のnamespaceに変数varとプロシジャprintが定義されています。
変数varとプロシジャprintはglobalスコープや他のnamespaceで定義されたそれとは競合しません。

namespace eval Hello {
    variable var {Hello World}

    proc print {} {
        variable var
        puts $var
    }
}
namespace eval Hello {
    variable var {Hello World}
}

proc Hello::print {} {
    variable var
    puts $var
}

namespaceの変数とプロシジャはnamespace名とスコープ解決演算子(::)を使ってアクセスします。

puts $Hello::var
=>Hello World
Hello::print
=>Hello World

エクスポートとインポート

以下もHelloという名前のnamespaceに変数varとプロシジャprintが定義されています。
namespaceのプロシジャはexport宣言をすると、namespace名を省略して呼び出せます。

namespace eval Hello {
    variable var {Hello World}

    namespace export print

    proc print {} {
        variable var
        puts $var
    }
}

呼び出し側は、import宣言をする必要があります。
importでプロシジャ名に'*'を指定すると全てのプロシジャにマッチします。

namespace import Hello::print
print
=>Hello World
namespace import Hello::*
print
=>Hello World

スコープ

Tclは、イベントハンドラを使ってコマンドを実行することがあります。
afterコマンド、bindコマンド、button -commandコマンド等が該当します。
イベントハンドラはglobalスコープで実行される点に注意してください。
たとえば、以下の例は、afterで1秒後にコマンドが実行されますが、
コマンドが実行されるのはglobalスコープなので、変数varは未定義エラーになります。

namespace eval Hello {
    variable var {Hello World}

    proc print {} {
        variable var
        after 1000 {puts $var}
    }
}
Hello::print

解決策としては、codeコマンドでnamespaceを事前に解決したコードを生成する方法です。

namespace eval Hello {
    variable var {Hello World}

    proc print {} {
        set cmd [namespace code {puts $var}]
        after 1000 $cmd
    }
}
Hello::print

この時、事前に解決したコード($cmd変数の内容)は以下の様になります。
inscopeコマンドは、指定されたnamespaceで引数のコマンドを実行します。

namespace inscope ::Hello {puts $var}