aiscript
aiscript copied to clipboard
エラー処理について
普通にthrow catchを実装してもいいですが、「エラー型」の実装ができそうなので提案します。
エラー型について
次のようなものを考えています。
- 何らかの式の評価でエラー
e
が発生したとき、通常の値の代わりにこの「エラー型」の値Error(e)
を返す。 - 「エラー型」の値は通常の値のように、変数に代入したり関数の引数や返り値にすることができる。
- 「エラー型」の値は通常の値とは違い、その値を参照して何か計算をしようとするとエラーを発生させる。そのエラーは最初の式の評価で発生したエラー
e
である。 - 値が「エラー型」であるかは
Core:type
を使用して見分けることができる。これにより例外処理を行う。
#11 で @syuilo さんが言っていたことを、既存のAiscriptコードの動作を壊さない範囲で実装する案です。
このエラー型の概念って他の言語にあったりしますか?
Either?
エラーを関数の返り値として表現している点ではEitherと似ていると思います。 ですが今回のエラー型は演算の対象となった瞬間にAiscript エラーで処理を停止させるため、Eitherのように成功とエラーを同列に扱うようなことはできません。
@syuilo 同じ概念を持つ言語に心当たりはありません。 単に #282 で言う「null以外の何かを返す」ためのものとしての発想です。
Core:type
だけ特別でそれ以外は専用メッセージを出す?
そうでないなら単に型エラーで済む気がする
型エラーだと原因が分かりにくいので…
Core:typeだけ特別でそれ以外は専用メッセージを出す?
~~その通りです。~~ 専用メッセージというか、これまでに出していたエラーと同じメッセージ(index out of rangeやNo such variableなど)を出します。
「null以外の何かを返す」が目的ならエラーチェックしないのはnull参照と同じで自己責任だと思う
普通の1 + "a"
みたいな式でもError
を返すということ?
だとしたら強制終了が遅延されるのはかえって分かりにくいような気もするかも?
強制終了が遅延されるのはかえって分かりにくい
それはその通りです
ただCore:type
での分岐を出来るようにするためにはそうせざるを得ないので…
基本的には計算した値はすぐ何かに使うと思うので、問題は軽いと思っています
「null以外の何かを返す」が目的ならエラーチェックしないのはnull参照と同じで自己責任だと思う
ごめんなさい、これよく分かってないです。解説希望です
「null以外の何かを返す」が目的ならエラーチェックしないのはnull参照と同じで自己責任だと思う
ごめんなさい、これよく分かってないです。解説希望です
Json:parse
で失敗した返値がError(e)
だったとしてこれを特別に扱う必要は無いと思う
Json:parseで失敗した返値がError(e)だったとしてこれを特別に扱う必要は無いと思う
次回参照された時にエラーメッセージを出す機能の話でしたら、まあ無くてもいいかもしれませんがあったほうが便利かなと思っています。
普通の1 + "a"みたいな式でもErrorを返すということ?
とりあえずはモナドのように失敗可能性のある処理にだけErrorを返させようと思っています。
復帰可能(Json:parse
)ならError、できない(1+'a'
)なら強制終了で区別した方がいいと思う。詳しくないけどGoがそんな感じらしいので。
err+1
みたいになってエラーが出るのはチェックしなかったことが問題なので普通の型エラー。
Error型を組み込みで入れるのは良いと思う。
1+'a'
のような事前回避が容易なエラーはその場で強制終了にするのは賛成ですが、復帰可能性の概念は曖昧で基準にしづらい気がします。何故Json:parse
が復帰可能で1+'a'
やゼロ除算は復帰不可能なのかよく分かりません。
err+1
のような事態で単純な型エラーにせずerrに内包されたエラーを出すのは、出来るだけ今まで同様にエラーを出したいという意図があります。(更新履歴を頻繁に確認する習慣がない人は、急に訳の分からない型エラーが出たら途方に暮れてしまうと思います)
とはいえ、これだと先で言われていたような「エラーの発生が遅延されてかえって分かりにくくなる」問題が発生してしまいます。そこで、例えば
number expected but got error. Error Message: "Unexpected end of JSON input"
のような複合のエラーメッセージを出すのはどうでしょうか?
基本的にはその場でエラーを発生させて、ユーザーの選択(専用の構文を使うなどして)でError型の値を返すようにするのはどうでしょう
try catchのように呼び出し元でのエラーキャッチができないと思います。これが少し気になりました
try {
someFunc()
} catch(e) {
processError(e)
}
でやりたいことは基本的に
let result=someFunc()
if (Core:type(result) == 'error') processError(result)
で出来るはずなのでその問題はないと思います。
できないは違いました ifで分岐、returnしてエラーを呼び出し元に伝える必要があるということですよね
エラーを例外ではなく返り値の型で表すことの利点の一つは(静的型チェックを行うシステムであれば)エラーの種類まで含めて型システムが働くことであり、try-catch文が混ざるとこの利点が消えてしまいます。
また、もう一つの利点は(静的型チェックがあれば)プログラマにエラーの処理を強制出来ることなので
ifで分岐、returnしてエラーを呼び出し元に伝える必要がある
これがむしろ利点になります。
(現状は静的型チェックがないのでただ面倒になっているだけですが…)
せめて
result? //if Core:type(result) == 'error' return result else result
catch(result) {...} // if Core:type(result) == 'error' {...}
みたいなシンタックスシュガーは用意すべきかも…?
基本的にはその場でエラーを発生させて、ユーザーの選択(専用の構文を使うなどして)でError型の値を返すようにするのはどうでしょう
これについてはどうですかね? エラーを処理しなかった場合にエラーの発見が遅れることを防げると思います。ユーザーが後でエラーを処理すると意思表示したときにだけエラーの発生を遅延させます。
throw-catchの話ではなく、基本的にはその場で実行を停止するようにしたいという話ですか?
エラー処理するまでもなくとりあえず動けば良いというようなスクリプトの使い方もあると思ってます。 この場合はエラーがその場で起こって欲しくないですか?
エラーを型で表す方式は厳格な型システムを前提としているので、「とりあえず動けば良いというようなスクリプトの使い方」とは思想が真っ向から対立するんですよね…
現状の使い方からするとメッセージ付きのnull
や-1
みたいな感じなので専用構文を挟まないとエラー処理できないのは分かりにくい気がします。
挟ませるとしてどの時点で停止扱いになってどの時点までに挟む必要がありますか?
専用構文でエラー処理を宣言するだけで厳密なエラー処理しようとしている書き手の意図は確認できますね その場合はエンジン側によるエラー停止はしないで良いんじゃないかと思います。 いつまでのエラー処理しないといけないという制約もあるのが理想ですが、実装が複雑になりそうです。
話を遡りますが、 @saki-lere さんの言っていた「復帰不可能な」エラーとは、「事前回避可能な」エラーをそう呼んでいるという認識で正しいでしょうか? そうであれば、そのようなエラーを即時停止の扱いにする理由が分かってきた気がします。(型システムに組み込むと型チェックが複雑になりすぎる)
@marihachi という訳で、エラーを「即時停止するエラー」と「処理すべきエラー」に分けようと思うのですが、どうでしょうか?
例えば、以下のような2種類を分けるということですかね 型違いの四則演算などエラー処理でなんとかするべきではないもの(常に即時エラー) JSONパースのような入力の違いによってエラーが起こりうるもの(エラー処理可能)
これについては賛成です