SuperColliderのエラー出力を理解した気になろう!

なんとなくコードを書いていたり、とりあえずコピペしてみて、いざ実行、でエラーが出力されると、エラーなんて読む気がなく、諦めてボケーッとどっかのサイト見てたりして気づいたら、もうこんな時間か!だったりしますよね。そうなる前になんとなくでも良いのでエラー出力を読んでみましょう。

まずは

{SinOsc.ar()}}.play;

よくやるのが、}を閉じ忘れたり、なんか1個多かったりですね。これは親切にマッチしないよ?と言ってくれるので、修正しやすいです。

/*
unmatched '}'
  in file 'selected text' line 1 char 14
・ ERROR: Parse error
  in file 'selected text'
  line 1 char 14:

  {SinOsc.ar()}}.play;
               ^
   
-----------------------------------
・ ERROR: Command line parse failed
nil
*/

}がマッチしなくて、それは何番目の文字か、を出力してくれますので、探しやすいですね。

{SinOsc,ar()}.play;

タイポするとパースエラーを返してくれるので、これの間違った部分がわかりやすいですね。

/*
・ ERROR: Parse error
  in file 'selected text'
  line 1 char 8:

  {SinOsc,ar()}.play;
         ^
*/

これも何番目の文字がダメかを出力してくれます。
この手の軽微なミスは間違いを見つけやすいので良いとして、内部のエラーの場合で、かつコードが長くなってくるとどこを修正していいのかさっぱり分からなかったりします。

きほん

SC3がユーザに返すエラーは3つに分類できます。
1. エラーメッセージ
2. エラーを引き起こしたメソッドのレシーバ、もしくはメソッドを呼んだ引数
3. コールスタック
※コールスタックについてはググってください。

例えば、下のbleachメソッドを実行してみましょう。なんかpostウィンドウにエラーメッセージが出力されます。結論から書くと、blechメソッドをレシーバである'1'がもっていないということが分かります。

1.blech
//1.blech(2)
/*
ERROR: Message 'blech' not understood.
RECEIVER:
   Integer 1
ARGS:
PATH: xxx
CALL STACK:
	DoesNotUnderstandError:reportError   0x104ca2798
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError   0x104ca29a8
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError   0x104ca2a58
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw   0x104ca2c68
		arg this = <instance of DoesNotUnderstandError>
	Object:doesNotUnderstand   0x104ca2f28
		arg this = 1
		arg selector = 'blech'
		arg args = [*0]
	Interpreter:interpretPrintCmdLine   0x116a92368
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "1.blech
"
		var doc = <instance of CocoaDocument>
	Process:interpretPrintCmdLine   0x116998148
		arg this = <instance of Main>
For advice: [http://supercollider.sf.net/wiki/index.php/DoesNotUnderstandError#blech]

*/

ここで、ちょっと復習してみましょう。レシーバ.メッセージ;というのが基本の文となります。この文は"レシーバ"というオブジェクトに対して、"メッセージ"という命令を実行させます。
要は、"レシーバちゃん"に対して、「おう、お前"メッセージ"しろよ!」と極悪プログラマがいっているわけですね。ここで問題となるので、"レシーバちゃん"が"メッセージ"という行動を理解しているかどうかです。
・"レシーバちゃん"が聡い場合
極悪プログラマに"メッセージ"しろと言われた"レシーバちゃん"は"メッセージ"という行動(メソッド)を理解しているため、
命令された後に"メッセージ"という行動(メソッド)をします。極悪プログラマが望んだ結果が返ってくるわけですね。
・"レシーバちゃん"が残念な場合
極悪プログラマに"メッセージ"しろと言われた"レシーバちゃん"は"メッセージ"という行動(メソッド)を理解できないため、
命令されても"ふぇぇ"とかいって、わけがわからないことを言い出しはじめます。
うん、お前は何を書いているんだ?と。ここでは"ふぇぇ"はpostウィンドウに出力されるエラー内容ですよ。"レシーバちゃん"がわからなかった内容を詳細に教えてくれているわけですね。ここでは"1"が"レシーバちゃん"で、"blech"が”メッセージ”のに該当します。"1"が"blech"メソッドを持っていれば、なんらかの結果が返ってきますが、持っていなければpostウィンドウにエラーメッセージが出力されます。

よむ

ERROR: Message 'blech' not understood.

ですが、これは素直に"レシーバちゃん"が理解出来なかったことが出力されます。"blech"ってなに、わかんない?ってことです。

RECEIVER:
   Integer 1

は、"レシーバちゃん"は何者?ということ。
ここでは整数の1が"レシーバちゃん"の正体であるとわかりますね。

ARGS:

"メッセージ"の引数です。
今回は"メッセージ"に引数がないので、ARGS:以下な何も表示されません。1.bleach(2);とか実行してみると

ARGS:
   Integer 2

と出力されます。

PATH: xxx

これは、ソースコードの場所ですね。気にしなくてもいいです。

CALL STACK:

コールスタックが逆順に表示されています。一番上のスタックは一番最近の呼び出し命令ってことですね。
大抵上から4つはの同じ呼び出し結果を表示させます。まあ、上から4つは無視していいってことです!
DoesNotUnderstandError:reportError
Nil:handleError
Thread:handleError
Object:throw
ですね。

コードブロックを選択して、ハイライトさせて、コードを実行しますよね。そうすると、コードはSCの裏側でごにょごにょされているんですけれど、その一部でInterpreterクラスのinterpretPrintCmdLineが呼び出されてたりします。
Process:interpretPrintCmdLine
Interpret:interpretPrintCmdLine
の部分も無視していいかと思います。呼び出されなかったりもしますが、ここでは気にしないで下さい。

で、コールスタックの内容が一番のキモなわけですが、まあ親切すぎてわかりにくいんですよ。

もうちょいよむ

Routine({
	var a;
	a = 5;
	loop {
		var b;
		b = 20.rand;
		b.postln.ecky_ecky_phtang;	// "NI!!!!"
		a.wait;
	}
}).play;
/*
a Routine
12
ERROR: Message 'ecky_ecky_phtang' not understood.
RECEIVER:
   Integer 12
ARGS:
PATH: xxx

上記参考

CALL STACK:
	DoesNotUnderstandError:reportError   0x12064f6d8
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError   0x11be47ea8
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError   0x11be4a448
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError   0x1208b51c8
		arg this = <instance of Routine>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw   0x120966ee8
		arg this = <instance of DoesNotUnderstandError>

とりあえず無視

	Object:doesNotUnderstand   0x120a81a18
		arg this = 12
		arg selector = 'ecky_ecky_phtang'
		arg args = [*0]

ここでエラーが発生しました。thisに12、selectorに'ecky_ecky_phtang'、argsに[*0]がそれぞれ代入されています。このオブジェクトは12で、セレクタとして'ecky_ecky_phtang'、引数はないよ、ってことです。で、セレクタの'ecky_ecky_phtang'は12がもっていないので、エラーになると。

	< FunctionDef in closed FunctionDef >   0x12045d7a8
		var b = 12

ここではbという変数に12が代入されています。変数に代入される処理があると、< FunctionDef in closed FunctionDef >が呼び出されるようです(たぶん)。

	Function:loop   0x120936c78
		arg this = <instance of Function>

ここでは、Functionのloopメソッドが実行されています。

	Routine:prStart   0x1206de328
		arg this = <instance of Routine>
		arg inval = 2421.955160245

この部分が、コードを実行したときに一番始めに処理されます。RoutineのprStartが実行されていることが分かります(prStartってなに?ですが、Routineクラスのメソッドですよ)。prStartの引数であるinvalに2421…が代入されていることが分かります。

For advice: [http://supercollider.sf.net/wiki/index.php/DoesNotUnderstandError#ecky_ecky_phtang]
*/

たんなる参考ですね。


CALL STACK以下の内容をもうちょっと細かくみてみましょう。CALL STACKは、下から上に沿って実行結果が出力されます。まず、XXX:YYY という部分ですが、これは "ClassName:MethodName"です。Routine:prStartはRoutineクラスのprStartメソッドだということですね。postウィンドウの"ClassName:MethodName"をダブルクリックして、cmd+jすると、ソースコードの該当メソッドを開いてくれます。
エラーになった部分の、Object:doesNotUnderstandですが、実はObjectクラスはdoesNotUnderstandメソッドを持っています。このメソッドを追っていくと、ERRORクラスが呼ばれてpostウィンドウになんかpostlnしているってことが分かったりします。
この"ClassName:MethodName"の下に、インデントされたargが数個出力されています。まず、大抵arg thisがありますが、オブジェクトはなにかを示していると思ってください。Routine:prStart部分ではRoutineのインスタンス、Object:doesNotUnderstandでは数字の12。this以降にさらにargがある場合は、MethodNameの引数になにが代入されているか、を示しています。prStartメソッドには引数としてinvalが内包されています。doesNotUnderstandメソッドにはselectorが。

おまけ

SCではエラー出力に、ErrorクラスかErrorクラスのサブクラスのインスタンスである、Errorオブジェクトを使います。
Error("This is a basic error.").throw;
これを実行するとERROR: This is a basic errorを出力されます、ERRORの部分に出力させるメッセージを自分でいじれるってことです。
他の言語と同じくtry、protect文を使って例外処理も書けますよ。

try { 1.blech; } { |error| error.postln };
protect { 1.blech } { |error| error.postln };