名前空間とは?
 昔のTcl/Tkは(いまもそうだけど)、 グローバル変数やプロシージャの名前はプログラム全体で共有されていたため、 大きなプログラムでスクリプトを source コマンドでロードしたりすると名前がぶつかって動作が変になったり、 同じ名前のプロシージャがあると勝手に古い方が新しい方に上書きされてしまったりする問題がありました。 そこでTclプログラマは名前がぶつからないように名付け方を工夫したり、 ぶつかっていないかチェックしたりという負担がありました。 そこでえらい人は考えて、Tcl 8.0にバージョンアップするさいに 「名前空間」(namespace) というものを発案し、プログラマが「この名前はこの範囲でだけ有効だよ」 と指定することができるようになりました。歴史的にはこの名前空間は Tcl/Tkの最も有名な拡張のひとつである [incr tcl] から Tcl/Tk本体にいわば「逆輸入」されたもののようです。

 Tclの名前空間は、本来の目的から言えば 「変数名やプロシージャ名が知らないうちにぶつかるのを避ける」 ということができればどのように書いてもOKなのですが、 実際にはJavaやPythonのクラスを模した、緩やかなオブジェクト指向パラダイミュを Tcl言語に持ちこむためにも使うことができます。

 簡単な、ていうか少し使い古された例ですが、 こんなサンプルを乗せてみます。

namespace eval MonthName {
    array set FullName {
	1 January 2 February 3 March 4 April 5 May 6 June
        7 July 8 August 9 September 10 October 11 November 12 December
    }
    proc GetByMonth {month} {
	variable FullName
	if {$month < 1 || $month > 12} {return {}}
        return $FullName($month)
    }
    proc GetByClock {ck} {
	variable FullName
	set m [string trimleft [clock format $ck -format %m] 0]
        return $FullName($m)
    }
    proc Now {} {
	return [GetByClock [clock seconds]]
    }
}
puts [::MonthName::Now]
puts $::MonthName::FullName([string trimleft \
 [clock format [clock seconds] -format %m] 0])
puts [clock format [clock seconds] -format %B]

 あまりに使い古された例なので、ナニをするコードかの説明は省きます。 本題のみということで!

●名前空間をつくるには
 ある区間のコードを名前空間で囲むには、 namespace evalでその範囲を大きく囲みます。 名前空間には上の「MonthName」のように名前をつけることができます。

 名前空間の中で、同じ名前空間のプロシージャを呼び出すには、 普通にプロシージャ名だけで指定すればOKです。上の例では、 NowプロシージャがGetByClockプロシージャを呼び出しています。

 名前空間の外、または別の名前空間からある名前空間のプロシージャを呼び出すには、 「::名前空間::プロシージャ名」とします(*1)。上の例では、

  puts [::MonthName::Now]
がこれを使って外から呼び出しています(*2)。
(*1)名前空間は階層的にすることもでき、その場合は 「::名前空間::名前空間::...::プロシージャ名」となります。 また「カレント名前空間」「親名前空間」などの概念もありますが、 ややこしくなるわりにあまり使わないので、ここでは省略します。
(*2)この例では、先頭の「::」は省略できます。

 上のFullNameのように、名前空間の中で直接定義されている変数は 「名前空間変数」(Namespace Variable)と呼ばれ、 名前空間内で共有することができます。

 名前空間内のプロシージャから同じ名前空間の名前空間変数が見れるようにするには、 プロシージャの冒頭部(*3)で、variableコマンドを使って 「この名前の変数は名前空間変数だよー」と宣言すればOKです。 上ではGetByMonthプロシージャなどがFullNameをvariableコマンドで宣言して使っています。

 名前空間の外、または別の名前空間からある名前空間の名前空間変数が見れるようにするには、 「::名前空間::変数名」とします。

puts $::MonthName::FullName([string trimleft \
 [clock format [clock seconds] -format %m] 0])

 現在いるコードがどの名前空間にいるかは 「namespace current」コマンドで知ることができます(*4)。

(*3)でなくても構いません。
(*4)ですから、[subst $[namespace current]::FullName($m)] のようにすれば、 同じ名前空間中の名前空間変数をvariableコマンドを使わずに指定しますが、 あまりそういうことをする人はいません。
というような感じで充分です。 8.0以降を使っている場合は積極的に名前空間を使うようにしたほうがよいでしょう。

●すげーありがちな落としアナ
 名前空間を使い始めると、 きっとGUIアプリケーション全体を名前空間で囲んでしまいたくなるはずです。 特にJavaでGUIアプリケーションを組んだことがある人は、 Tcl/Tkでも好んでそうするに違いありません。

namespace eval Hello {
    set Message {Hello, World}
    proc Init {} {
	label  .laba -text "Push this button:"
	button .cmda -text {Here} \
	    -command "[namespace current]::ShowDialog"
	pack .laba .cmda
        bind .laba <Enter> { Hello::MakeRed    %W }
	bind .laba <Leave> { Hello::MakeNormal %W }
	bind . <Key-q> exit
    }
    proc MakeRed {w} {
	$w configure -fg red
    }
    proc MakeNormal {w} {
	$w configure -fg black
    }
    proc ShowDialog {} {
	variable Message
	tk_messageBox -message $Message -title hello
    }
}
Hello::Init

-commandで指定される文と、
bind {} の中の文は、
書かれている位置が名前空間内でも、
名前空間の外で実行される

とゆーことは、いくら強調してもしすぎるとゆーことはありません。
確か英語の構文にあったやんね(池)
また脱線しようとするー(浦)

これはエラー:
    button .cmda -text {Here} \
        -command ShowDialog
これはOK:
    button .cmda -text {Here} \
        -command "[namespace current]::ShowDialog"
これはエラー:
    button .cmda -text {Here} \
        -command {[namespace current]::ShowDialog}
う〜ん、奥が深うございまする。
もいっちょ これはエラー:
   bind .laba <Enter> {MakeRed %W}
これはエラー:
   bind .laba <Enter> {[namespace current]::MakeRed %W}
これもエラー。「%W」が認識してもらえません:
   bind .laba <Enter> "[namespace current]::MakeRed %W"
これはOK:
   bind .laba <Enter> { Hello::MakeRed %W }
ですが、「Hello::」と名前空間をべたに指定するのがイヤな場合は、 「namespace code」コマンドを使った奥の手があります。
    bind .laba <Enter> "[namespace code {MakeRed %W}]"
namespace codeコマンドは、与えた文をその namespace code コマンドが実行されたときの名前空間に合わせて自動的に変形するコマンドです。 上の例では名前空間 Hello 内で namespace codeコマンドが実行されたので、 $a の内容は「::Hello::MakeRed %W」と正則の名前空間の名前が自動的に補われます。 %Wがそのまま残る点に注目! このように、 マニュアルを覗いてみるとややこしい状況が発生するのを先回りして読んだように 「逃げ道」が用意されているのがわかって面白いですよ。 この namespace code コマンドなどは、 おそらくこういう状況を想定して用意したものだろうことが強くうかがえます。 ていうか、ぷんぷんにおいます。

やねうらの top
(first uploaded 1999/06/18 last updated (not ever), Urano398)