k.inabaさんのthisを返すがとてもイイ、というか、入れなきゃダメな機能である気がして入れてみました。
sukuna-0.4.5b
「selfを返す」+「その引数そのものを返す」の両方に対応しました。
これらがあると懸案だったアレやコレやダメポな部分がまとめて解決しそうなのです。
k.inabaさんの例を使うと、これまでは、
class Stream
{ print: | object: obj -- Stream: self |
obj :print
self
}
end
class HogeFugaStream super{ Stream }
{ printHoge: | -- HogeFugaStream: self |
self
}
end
↑こんなクラスがあるときに、
{ test
" bbb" " aaa" 123
HogeFugaStream :new :print :print :print
}
↑これは普通にVMコードにコンパイル、実行できますが、
{ test2
123
HogeFugaStream :new :print :printHoge
}
↑こいつはコンパイル時に、「printHogeなんてシラネーヨ」みたいな警告がでます。
この時の:printHogeは、実行時メソッド探索呼び出しにコンパイルされるので、実行はできますが、printHogeが見つからなかった場合には実行時エラーになってしまいます。
加えて、実行時メソッド探索呼び出しにコンパイルされてしまうと、そこで静的型チェックが必ず失敗するようになるので、自動的にワードの型付けをする機能の恩恵が受けられなくなります。(´・ω・`)
しかし、新しい「selfを返す」機能を使うと、
class Stream
{ print: | object: obj -- 'self |
obj :print
self
}
end
class HogeFugaStream super{ Stream }
{ printHoge: | -- 'self |
self
}
end
↑こんな風に書けて、上のtest1もtest2も警告無しでコンパイルできるようになります。
printHogeは、vtableを使った仮想関数呼び出しにコンパイルされ、静的型チェックも(型が整合しているなら)失敗しなくなります。
このため、上のtestとtest2の場合には、
: test | -- hogefugastream: | : test2 | -- hogefugastream: |
↑こんな感じでどちらもStreamではなくHogeFugaStreamを返す型が自動で付くようになります。
(自動型付けは制御構造が出てくると必ず失敗するようにしてあるので、使用できるところはとっても限定されますけれども。)
この「this(self)を返す」がないとダメポなパターンとして、mixinの中のメソッドがselfを返すパターンがあります。
mixin foo
{ bar: | -- どんな型を返せばいいんだろう? |
self
}
end
class bar super{ foo object }
{ baz: ." baz" cr }
end
barメソッドの返す型をfoo型にしてしまうと、barクラスで定義されたメソッドbazをカスケードしようとした場合に「メソッドみつかんねー」と怒られることになります。
fooは様々なクラスにmixinされる可能性があるので、barメソッドの定義の時点ではselfの具体的な型は静的に決定できません。
なので今まではany型を返してお茶を濁していました。しかしany型を返してしまうと「実行時メソッド探索呼び出しにコンパイル→静的型チェックが必ず失敗」のコンボが決まってしまいます。Orz
ところが、今度からは、
mixin foo
{ bar: | -- 'self |
self
}
end
↑こう書けます!ありがたや!
ていうかmixinと静的型を入れようとした時点で自分で考えておくべきことですよね・・・。
もう一つ。「その引数そのものを返す」があると、例のアレがナニします。
スタック操作ワードを定義するとき、
{ flip | a b -- b a |
b a
}
こう書いてしまうと、このflipの型は、
: flip | any: any: -- any: any: |
こうなってしまっていました。このままだと、
{ test 10.0 10 flip }
↑こんな風にコンパイル時にスタックの型が決められそうなときでも、
: test | -- any: any: |
すべてがanyになってしまいます・・・。Orz
でもこれからは、
{ flip | a b -- 'b 'a |
b a
}
↑こう書けます!
このflipの型は、
: flip | any: any: -- '2: '1: |
このようになります。
2番目の引数の実際の型を持つ値と1番目の引数の実際の型を持つ値を返す、という意味になります。
表記の仕方に悩みましたが、最終的にStrongForth風味にしました。
というか最初からStrongForthを参考にすれば良かったような気がしないでもなく・・・。
今度は、
{ test 10.0 10 flip }
↑これが、
: test | -- int: float: |
↑この型を持つようになります。
というわけで、k.inabaさんのおかげでスッキリしなかった部分がかなりスッキリできた気分です。