CUIなSmalltalkでメソッド調べる小技

## はじめに
Smalltalkというと

> "独自のGUIで仮想イメージとかなんか閉じ籠ったアーキテクチャでしょ？"
> "OSのシェルとかと相性悪そう。"

というイメージがあるかもしれませんが、Smalltalk一族の中にもシェル環境で動く CUI系の処理系がいくつかあったりします。ターミナルでプロンプトを通してコンピュータ資源をオブジェクトとしてフニフニいじる。この手の処理系のなんとも言えない魅力だったりします。
けれど逆に、GUI組が備えている(そして、今ではIDEとして多くの処理系が持つ)システムブラウザがなかったりします。なので、リファクタリングやら、メソッド探したりはどうすんでしょう？テキストgrepですかい？というツッコミどころもあるのですが、そこはそれSmalltalkです。クラスですらオブジェクトで自己記述できる世界です。オブジェクトそのものがメソッドについて教えてくれるように支援してもらえばいいわけですね。といういうことでそんな拡張のチャレンジしてみました。
Smalltalkプログラミングでよく使うシステムブラウザの「センダ...」メニュー, 「インプリメンタ...」メニューのようなものを想定してます。
あるメソッドがどこに実装されているのか？それがどんな風に使われているのか？を調べるものです。
![SystemBrowser.png](https://qiita-image-store.s3.amazonaws.com/0/13174/3d167167-2446-1a84-75dc-83eae1c0b196.png)

## 対象処理系
対象処理系は GNU Smalltalk です。GNU Smalltalk については以下のサイトを参考にしてください。

http://smalltalk.gnu.org
http://gst.plasticheart.info

## init.st のセットアップ
GNU Smalltalkではホーム直下に.stディレクトリを作成してそこにinit.stというファイルを置いておくと起動時に読み込まれて実行されます。
今回は拡張をこの init.st に定義しました。
gist に [init.st](https://gist.github.com/tty0hayashi/d68b4a4516bfe0eba8ca) でアップしましたので、このファイルをダウンロードして置くか、すでに自前の init.st をお持ちの方は内容をコピペで追記してください。

## まずは拡張無しで簡単にできること
クラスの検索とかメソッドの一覧は拡張無くても簡単にできます。
例えばクラス名の一部にDateを含むクラスの一覧。
(以下、st> の部分はプロンプトでそれより右側が入力内容になります)

``` smalltalk:
st> Smalltalk keys select: [:name | '*Date*' match: name]
```
Dateクラスのメソッド一覧はこんな感じ。

``` smalltalk:
st> Date selectors 
```

## メソッドの探索
ここからは拡張したメソッドを利用します。
クラスは分からないのだけれど、メソッドの名前の一部に day を含むメソッドを調べるには fndMthd メソッドを使います。

``` smalltalk:
st> 'day' fndMthd
```

実行すると CompiledMethod のリストが SortedCollection でソートされて返って来ます。
こんな感じです。

``` smalltalk:
st> 'day' fndMthd
SortedCollection (Date class>>dayOfWeek: Date class>>daysInMonth:forYear: Date class>>daysInMonthIndex:forYear: Date class>>daysInYear: Date class>>daysUntilMonth:year: Date class>>today Date class>>utcToday Date class>>year:day:hour:minute
* 略 *
```
例えばリストの先頭にある Date class>>dayOfWeek: というのは、Date クラスのクラスメソッド dayOfWeek: を意味します。

見づらいな〜という時は適宜加工しましょう。displayLinesとかは改行して出力してくれたりします。

``` smalltalk:
st> 'day' fndMthd diplayLines
```

また'days'のところには正規表現パターンが使えます。
例えば、先頭が day で始まるメソッドならば

``` smalltalk:
st> '^day' fndMthd
```

day の前に何か文字列がある場合はこんな感じでしょうか。

```smalltalk:
st> '[a-z]day' fndMthd
SortedCollection (Date class>>today Date class>>utcToday DateTime class>>today )
```

## ソースを見る（実装を調べる）
CompiledMethod に methodSourceString を送ると該当するメソッドのソースが見れます。
上のリストの1番目の Date class>>today のソースならば、以下のようになります。

```smalltalk:
st> '[a-z]day' fndMthd first methodSourceString
'today [
     "Answer a Date denoting the current date in local time"

     <category: ''instance creation (Blue Book)''>
     ^self fromSeconds: Time secondClock
    ]'
```

ちなみに methodStouceString では長いので init.st の中で src という名前で再定義しています。
またリストではなくて任意のクラスのメソッドのソースを見るには、例えば Date クラスのインスタンスメソッド dayOfWeek ならば、以下のようにします。

``` smalltalk:
st> (Date >> #dayOfWeek) src
'dayOfWeek [
     "Answer the day of week of the receiver. 1 = Monday, 7 = Sunday"

     <category: ''date computations''>
     ^(self days + 1) \\ 7 + 1
    ]'
```

## メッセージ送信元の検索（メソッドの使われ方を調べる）
次はメッセージの送信元の検索ですが、dayOfWeek を使っている箇所を調べるには、次のようにメソッド名に snds を送ります。

``` smalltalk:
st> 'dayOfWeek' snds
SortedCollection (DateTime>>dayOfWeek )
```

どうやら、Date の dayOfWeek はシステム内では、 DateTime の dayOfWeek でしか使われてないらしいです。この DateTime の dayOfWeek の実装は次のようになってますね。

``` smalltalk:
st> (DateTime >> #dayOfWeek) src
'dayOfWeek [
	"Answer the day of week of the receiver. Unlike Dates, DateAndTimes
	 have 1 = Sunday, 7 = Saturday"

	<category: ''computations''>
	^#(2 3 4 5 6 7 1) at: super dayOfWeek
    ]'
```

## おわりに
こんな具体にCUIで対話的にメソッドを探索することができます。もしかしたら、マウスを使わずキーボードから手を離す必要がないので、場合によってはこっちの方が便利ということもあるかもしれません。
なお、gist のソースを見れば分かりますが、どれも String クラスにちょっとした拡張を加えてるだけです。環境をいじるなんてレベルではありません。でも、こうした小技をinit.stに集めるというのも楽しいものであります（上では紹介できてない拡張もいくつか入ってますが、短いので見ていただければ分かるかと）。

そして、できればもっと踏み込んで REPL の仕組みを作り込んで独自のシェルを構築する……などということも考えられるかと思います。

### 【参考】その他標準でのメソッド
クラスのインスタンス変数名などを調べるには以下の通りです。これらは標準のメソッドです。

* インスタンス変数名の一覧(=例= Date の場合)

``` smalltalk:
st> Date instVarNames
(#days #day #month #year )
```

* クラス変数名の一覧(=例= Date のクラス変数名)

``` smalltalk:
st> Date classVarNames
IdentitySet (#MonthNameDict #DayNameDict )
```

* すべてのサブクラスの一覧(=例= Date のサブラクス)

``` smalltalk:
st> Date allSubclasses
Set (DateTime )
```

* すべてのスーパークラスの一覧(=例= Date のスーパークラス)

``` smalltalk:
st> Date allSuperclasses
OrderedCollection (Magnitude Object )
```