kiyokaのブログアーカイブ

Archive of old blog posts

コードブロックに副作用が混在するかを検出できるかどうか

先日のScalaの本からの流れで勝手に盛り上ってしまっている話題を前へ進めてみる。

さて、[Nendo]にimmutableなコードブロックを保証するスペシャルフォームを追加したいと考えている。 こんな感じ。もしコード中に破壊的操作を行うメソッドが混在していたら、例外を発生させる。

(immutable S式)

immutableは[Nendo]の予約語で、引数に取ったS式はマクロ展開されたあと破壊的操作が無いかを検査される。 ランタイムではなくて、コンパイルフェーズでチェックができればベスト。 これができたら画期的だと思う。 やりたい理由は、テスタビリティの確保だ。破壊的操作が無いコードはテストコードが非常に書きやすい。

破壊的操作があるプログラムは、環境を用意するのも一苦労だ。 以下の引用は、テスト環境を用意するのが大変という話を説明するのにもってこいのやつ。

Joe Armstrong 「再利用性の欠如はオブジェクト指向言語から来るもので、関数型言語では 話が違います。オブジェクト指向言語の問題は、それが周りに引きずってい る暗黙の環境にあります。バナナが欲しかったのに、手に入れてみたら、バ ナナを握ったゴリラと、それにジャングルまでついてきたというようなもの なのです。」 つまり、オブジェクト指向の方法ではテストデータや環境を準備するための依存を断ち切るのが非常に困難だということだ。

話を戻して、破壊的操作のある/なしを切り分けることが本当にできるのか?というのは、今はできると思っておいたほうが楽しいので、しばらくはつっこまないで頂きたい(笑;) 難しいと思っている理由は、いろいろあるんだけど、[Nendo]はRubyへのトランスレータとして実装しているので、ターゲットが生粋のオブジェクト指向言語だというところ。

オブジェクト指向は、基本的にはオブジェクトに状態を持つパラダイムであり、オブジェクトの状態を変更しながらプログラムが走行させるのが基本だ。 つまり、状態の破壊的操作が基本にあるということ。 かたや、私がやりたいのはあるコードブロック内では非破壊的なメソッドだけを集めてプログラミングしたいという要求であり、Rubyを選んだ時点で矛盾があるような気もするが… (ただ、似たような状況に立っている言語のScalaがうまくやっているということわかったので少し楽観的だ)

図で説明してみる。 img

img

img

いくら書いても、具体的にどうやればできるという話が出てこないけれども、それはこれから考えるのだよ(笑)。 最後の手段としては、人間が慎重に破壊的操作の無いメソッドを選び出してリストを作るということになるのだけど… ブラックリストではなく、ホワイトリストになるのかな。 因みに、ScalaではSet/Mapはひとつの親クラスからimmutable版とmutable版のそれぞれをtraitで継承して作りだしている。Listはimmutable版しかなくArrayはmutable版しかない。あとはプログラマが慎重に部品を選びなさいというような感じだと思う。(本を読んだだけのレベルだけど)

[Nendo]はどうするか。これからじっくり考えます。


コメント by shiro:
コンパイル時にやるには、型情報があった方が圧倒的に楽ですね。生のSchemeの場合、そこから参照している手続きを全部追っかけていかねばならず、結局それはその場で一種の型情報をつけようとするのと同じですから。参照透明かどうかって情報だけ追えば良いので完全な型付けほど大変じゃないですが、外から引数で渡される関数なども追っかけることになるので、グローバルなプログラムの解析になりそうです (外から渡される関数引数に型情報をアノテートできるようにしていれば別ですが)。

あと、immutableは「対象が変更不可」という形容詞だと思うので、変数やオブジェクトに対して使う形容詞であって、「式」に対して使うのは違和感があります。式に対して形容するならreferentially transparentが正確だと思いますが、長いので (pure <式>) とかかな? (no-side-effect <式>) はちょっとださいし。


コメント by kiyoka:
コメントありがとうございます。

shiroさんのコメントを読むまで、外から渡される関数に思い至っていませんでした。そうか、それもありますね。 参照透明かどうかをグローバルに解析しないといけないのは大変ですね。Rubyのライブラリクラスのメソッドが参照透明かどうかを自動で検査するのも難しいという問題もあります。 やはり参照透明な関数や変数(というか定数)をリストアップするホワイトリスト方式が有力なのかもしれません。 それはある意味、型情報のアノテーションを手作業でやるということに相当するのかな。 ホワイトリストなので、必要な分だけをアノテーションすればいいので始めやすいですが、関数が増えるごとに手作業で追従しないといけないのが難点です。 漏れが出ると便利さが半減するので…


コメント by kiyoka:
もうひとつ。immutableは形容詞ですね。(pure <式>) がかっこいいので実現できたらこれにするかもしれません。但し実現できたらの話ですが。 :)

コメント by shiro:
コンパイル時にやるには、型情報があった方が圧倒的に楽ですね。生のSchemeの場合、そこから参照している手続きを全部追っかけていかねばならず、結局それはその場で一種の型情報をつけようとするのと同じですから。参照透明かどうかって情報だけ追えば良いので完全な型付けほど大変じゃないですが、外から引数で渡される関数なども追っかけることになるので、グローバルなプログラムの解析になりそうです (外から渡される関数引数に型情報をアノテートできるようにしていれば別ですが)。

あと、immutableは「対象が変更不可」という形容詞だと思うので、変数やオブジェクトに対して使う形容詞であって、「式」に対して使うのは違和感があります。式に対して形容するならreferentially transparentが正確だと思いますが、長いので (pure <式>) とかかな? (no-side-effect <式>) はちょっとださいし。

コメント by kiyoka:
コメントありがとうございます。

shiroさんのコメントを読むまで、外から渡される関数に思い至っていませんでした。そうか、それもありますね。 参照透明かどうかをグローバルに解析しないといけないのは大変ですね。Rubyのライブラリクラスのメソッドが参照透明かどうかを自動で検査するのも難しいという問題もあります。 やはり参照透明な関数や変数(というか定数)をリストアップするホワイトリスト方式が有力なのかもしれません。 それはある意味、型情報のアノテーションを手作業でやるということに相当するのかな。 ホワイトリストなので、必要な分だけをアノテーションすればいいので始めやすいですが、関数が増えるごとに手作業で追従しないといけないのが難点です。 漏れが出ると便利さが半減するので…