#可変長引数、値を返さない手続を使わないとか、 if とか cond とかに制限を加えて使っうという前提でなら、普通に型推論できそうですね。
#「可変長引数、値を返さない手続を使わないとか」< この条件で既存の実用規模のSchemeコードはほぼすべて除外されちゃう。最初からコミットして徹底的にそういう方針で開発するのでない限り。
#もちろん、そういう前提。3> 最初からコミットして徹底的にそういう方針で開発するのでない限り。
#横から申し訳ありません。
#でも、それなら、Liskell?
#たぶん、可変長引数は型推論可能だと思います。例えば、リストのような形で推論するとか。引数が全部同じなら確実でしょう。
#ということですね。ある種別の言語って感じ。それはそれでいいんだけれど、「現在あるSchemeで型推論をやりたい」っていう要望とは別の話になっちゃいますね。
#s/引数/引数の型/
#えーっと、上の方にも書きましたが、可能か不可能かなら型推論可能なことは自明なんですよ。問題は「実用上役に立つと思える程度まで細かく推論できるか」という話でして。
#申し訳ありません。確かに結構重くなってしまうので、実用的かというと判らないです。
#重くても有用な結果が出るならいいんですが、(apply map proc args) とかやられたらprocの型は(関数であること以外は)わからなくなっちゃうよね、みたいな話です。
#型推論の自明な解とは、Schemeの場合全ての型を含みうる型(Gaucheなら<top>)があるので、全部その型にしちゃえば解になるって話です。
#確かに、戻り値は逆側から推論してわかる可能性がありますね。あとは、procの定義を解析すればどうでしょうか?
#失礼しました、procの定義を解析すればどうでしょうかはぼけてました。無視してください。
#gradual typingの方でノウハウは色々あるみたいなんで、頑張れば結構便利にはなると思います。たぶん。どこまで頑張るかって話のような。
#私の関心としては、簡潔な表現を追っかけてくとオーバロードがどんどん重なってく感じがしてて(例えばコレクションを関数としても振る舞わせるとか。Gaucheでもregexpが関数として呼び出せたりしますが)、でもそれは静的解析とはかなり逆の方向なんで、動的な言語には動的なりのやり方があるんじゃないかなと思ってます。
#コレクションやregexpを関数として呼び出せるなら、静的にそれらがコレクションやregexpであると判れば、よりうれしいようなきがしますがどうでしょうか?
#それとは別に動的な言語にはもっといいやり方があるというのはわたしもそう思います。少なくともtracing jitはより良いと思っていますし、もっといいやり方があるようなきもします。
#そうなんです。静的にわかるとかなり色々最適化ができるので。ただ、(define (fn p) ... (p x) ...) と引数でpが渡された場合、それが通常の意味での関数に限定されなくなるって意味では型推論とは相性が悪いですね。
#<regexp>が <string> -> Maybe <regmatch> のsubtypeでもある、と考えれば矛盾は出なくはあるのか。
#pの部分に変数が使えるようになった時点でpが関数に限定されなくなってもそれほど支障は無いんじゃないかなと思います。逆に言えば、pが変数になりうる場合の型推論はすごく難しいといえると思いますが。
#ふーむ、何となく、私が欲しいのはinter proceduralな解析かなという気がしてきた。同一手続き内で、例えば(let ((p #/rx/)) ... (p x) ...) というのを見て、p::<regexp>, x::<string> というのはかなり簡単だと思うんですね。(そもそもpがset!されてなかったらGaucheは上の式を(#/rx/ x) へと最適化しちゃいますから)
#で、欲しいなと思うのはさっき上げたfnがあって、別の関数内で (fn #/rx/) みたいに呼び出している場合をも検出して、specializeしたfnを生成する、みたいな話かなあと。
#静的言語なら少なくともモジュール単位でそれが可能なんで、それが羨ましい。
#型推論できるできないではなくて、型推論した結果を何にどのように使うかという問題でしょうか。
#specializeしたfnwoがリテラルで呼び出しているときは
#すみません、操作ミスです。specializeしたfnを用意して、リテラルを引数に呼び出しているときはそっちを使うというのはすぐ出来そうな気がします。ただ、効果があるかは疑問ですが。
#いや、呼び出し側がinner loopにあるってケースは結構あると思いますよ。経験上、こういう最適化ができないためにマクロなどをつかってinner loop内に無理やり展開するってケースはよくある気がします。
#もっと、詳しく推論して可能なところからspecialize版を呼び出すというのは多分かなりの頻度で可能になるんじゃないかなと思います。
#ああ、そうそう、(fn #/rx/) みたいなリテラル呼び出し自体は少ないでしょうけれど、(fn a) って呼び出しのaが追っかけてったら<regexp>だった、みたいなケースですね。
#手続の返り値でディスパッチ?
#そのコンテキストを型推論する
#それをやるならデータフロー解析が必須ですので、エスケープ解析とかひっくるめてやるとかなり強力そうですね。
#stalinは全部やってるんですよね。
#やっぱり、解析のパフォーマンスですね。何かいい方法はないものかいつも思います。
#SchemeではMaybeの代わりに#fを使うことがよくある気がします。たとえば、assocとかmemberとか。regex系もマッチしなかったときに#fを返しますよね。また、再帰させるときも番兵としてnullかconsセルかを判定させます。そういう意味で、Schemeに型推論を適用する1手法として、「式xは○○か××である」という「制限された総称型」(要するに型のor)を導入する必要があるのかな、と。
#型のorがあれば、「<regexp>が <string> -> Maybe <regmatch> のsubtypeでもある」ってのは処理できるはず。ただ、型のor自体がうまくworkするかどうか、ちょっと予測できないでいます。たとえばorされた型Aと別の型Bとの比較はどうあるべきか、とか。orされた型どうしの比較はどうあるべきか、とか。
#また、ぼく自身が型推論を欲しい理由は、やっぱり脳力の節約です。Schemeで大きなアプリケーションを作ったりしていると、把握できなくなっちゃうんですよね。それで、把握できなくなると嫌になって、開発がストップしてしまったり、逆説的に小さいアプリケーションばっかり作ってたりします。
#あとは、昔作ったモジュールとかを改造・修正する必要が出てきたときに、どんなデータ構造を使っているか、なかなか理解できなくなってしまったり。
#逆にHaskellでプログラミングをしていると、このへんが非常に楽に感じます。ある程度機能を増やしたくなったら、モジュールに分割してしまえばいいし、モジュール化したコード内容についてはさっぱり忘れてしまっても問題がありません。
#データ構造の理解の助けのためだと、型推論よりむしろ推論された型をわかりやすく表示する方法のほうが重要になってくると思います。Haskellなどと違ってSchemeでは型が非常に複雑怪奇になりえると思います。テキストだけではなくグラフィカルに表示することを検討すべきだと思います。
#もし何か良い資料があればぜひ知りたいです。
#型を分かりやすく表示させることも意味があるでしょうけど、機械的な型チェックができると助かるかな、と思っています。
#代数的データ型のないSchemeでは、データ構造をうまく解析して表示させるのは難しそう…。逆にいうと、その機能は代数的データ型の不在を補うものになるでしょうから、Schemeそのものに代数的データ型的な機能(レイヤ)を追加する必要があるのではないかな、とか。
#プログラミングのし易さという意味でいうと、ぼくは結構いつもGaucheのマニュアル(Function and Syntax Indexの項)とにらめっこしながらコードを書いています。各関数の引数と戻り値の型を確認するためです(基本的なのは覚えてますけど)。
#でも、もし今後モジュールがもっとたくさん増えたとしたら、引数と戻り値を把握したり、それらの型を含んだデータ構造を把握するのは大変だろうなと想像します。せめて、Emacsのエコー領域に関数の引数と戻り値の型が表示されたら楽になるかな、とか。
#Haskellには、野良モジュールというか、同じような機能を実装したモジュールがたくさんHackageに登録されていますが、Schemeではなかなかそういう状況にならない。なんでだろうなーとか考えてみると、やっぱり1つの理由は型なのかなという気がします。
#動的な言語で型チェックを行うと、言語仕様上OKのものが型エラーになるわけですが、どこまでが正しくてどこからが誤りかを決めるのはなかなか難しいなと思います。代数的データ型を導入した場合、何でも入るリストはおそらく型エラーになるでしょうが、果たしてそれがいいのかどうか。
#Haskellだとモジュールにしないといけない機能が、Schemeだとconsセル等の既存のデータ構造の組み合わせだけで出来るというのもあると思います。再利用や保守を考えるとどっちがいいかよくわかりませんが。
#うーん、個人的にはどんな種類の型を受け取るのか、返すのかって、関数名や変数名から何となく類推できるので、あんまり困ったことがないかも。Gaucheのマニュアルはよく参照しますが。
#モジュールの件はあんまり動的型・静的型とは関係ないような気がする。Schemeはおいておくとして、Perl, Ruby, Pythonでは大量に野良モジュールが存在しますし。
#Schemeは単に、各処理系で開発者が分断されてしまっていることのほうが大きいんじゃないかな。
#うーん、確かに。>モジュールの件はあんまり動的型・静的型とは関係ない
#自分でもHaskellの良さをうまく表現できないんですけど、なんか使い勝手が良いんですよねぇ…。
#まぁ、Haskellにも型が厳格ゆえの使いにくさもあるけど。
#私は、Schemeでコードを書くときって、小さな単位の関数をちょっとずつ作っては動作を確認して、またそれをもとに関数を書いていく、という感じで書いていきます。
#で、その過程で gosh で動かしたり、単体テストを書いていったりしますが、その内容そのものが API の使い方になるので、コードを書いていて「量が問題で」データ構造が分け分からなくなる、といったことは少ないような感じがします。
#設計をミスして分け分からなくなることはよくありますが。
#Haskellでコードを書かないので、HaskellとSchemeとの比較はできないんですけど、Schemeって開発スタイルはSmalltalkに近いんじゃないかなと思ってたりします。
#なので、Schemeでの開発って、厳密な静的検証でコードをチェックというより、「常にコードが動いている状態で動作を確認する」というスタイルが合っているんじゃないかなと思います。
#じゃなんで glint なんて作ったんだよ → 自分
#以前ニコニコ動画で、koguroさんがライフゲームをコーディングされているのを拝見しました。面白かったです。
#あっ、どうもありがとうございます。みんな自分のコーディングスタイルをさらしてくれれば、面白いのになー、と思ってあげてみました。
#ぼくの場合は、コーディングスタイルが違うのかなぁ。量が問題でデータ構造が訳分からなくなる、ということはないのですが、コーディングしながら仕様や関数APIを変更したくなるときが多いです。で、関数やデータ構造を変更するわけですけど、途中で嫌になっちゃうんですよねぇ。
#1から書くのは楽しいけど、修正するのに苦痛を感じるのかもしれない。
#あー、それはよくあります。
#修正するときに型チェッカがあれば、楽になるのかなーと思ったり。
#うーん、型チェッカがあるのは便利ですが、理想論をいってしまうと、データ構造がちゃんと隠蔽できていれば、修正箇所って外部とのインタフェース部分になるので、局所的な修正ですむことが多いんじゃないかな。
#とはいえ、私はいつも泣きながらコードを検索して大量に修正をかけていますが...
#そうかもしれないですね。理想論としては…。
#あと、「仕様やAPIの変更で修正箇所を特定したい」という目的なら、命名規約でなんとかできそうな気もする。
#まあ、Javaとかで型をちょこっと変えたときにコンパイルエラーがわらわら出てくれると、修正箇所の特定が楽でいいのは確かです。
#ただ、なんというのかなぁ。たとえばhttpGetをする関数を書いたとして、後で「やっぱりuser agentを変更できるようにしたい」とか、「statusが3xxのときの動作を変えたい」といった変更が発生したとき、
#関数を修正するのか、それとも別のラッパー的な関数を追加して対応するのか、みたいな問題が起こります。そのときに、関数名の付け方とかがad hocにhttpGet2とかになりがちで、「うがー!」ってなったりします。
#これはちょっと、上で挙げた「修正箇所が特定できないしんどさ」とは違う問題ですが。
#名前を考えるのは確かにしんどい。
#AI的なアプローチで、相談すると適当な名前を考えてくれるエージェントとかないかしらん。
#Elizaみたいに、相談するときの文章の単語を適当に抽出すれば、それっぽいのできないかな、と思うことがあります。
#RT : koguro: なので、Schemeでの開発って、厳密な静的検証でコードをチェックというより、「常にコードが動いている状態で動作を確認する」というスタイルが合っているんじゃないかなと思います。 http://bit.ly/1jQ9er #RT : ふじさわ: 以前ニコニコ動画で、koguroさんがライフゲームをコーディングされているのを拝見しました。面白かったです。 http://bit.ly/15NTH6 #あ、ごめんなさい。こっちに投稿されるとは思わなかった。
#twitterよくわかってなくていろいろ遊んでたら暴発しました。
#名前の付け方とか、レイヤの分け方が予測できない・分けにくいという状況は、たぶん問題の全体像を把握できていないってことなんだろうなぁ。でもそれは、ある程度避けられないことだと思います。
#Haskellで型チェック機能を使っていると、関数自体の修正も楽だし(型チェックがある程度デバッグの代わりになる)、ラッパー的なレイヤ違いの関数を作るのも楽になります(関数名にあまり気を遣わなくてよいので、httpGet2でもそう問題が起きない(型を見れば何をする関数か分かるので))。といったメリットがあるかな。
#型チェックで、前者の修正箇所の特定が楽になるのは確かですね。ただ、後者の「名前以外でも、何をする関数か分かる」という点については、Emacs側でなんとかできそうな気がする。
#自作のelispで、Gaucheのコードやinfoから eldoc を生成して表示するようなのを使っているんですが、そういった使えば、引数の名前からある程度類推できるかも。
#あと、型チェッカがあると、httpGet関数自体を修正してAPIが変更になった場合に、そのモジュール自体のバグ検出だけじゃなくて、httpGet関数を呼んでいる呼び出し側のバグ検出も静的にできるようになります。もしそのようなアプリケーションやライブラリがたくさんある場合に、便利なんじゃないかなぁ、と。
#お、それはいいですね。>自作のelisp
#でも逆に言えば、関数の引数とか戻り値をすぐ確認できるツールは、やっぱりあったほうが便利ということかなぁ。我田引水。
#学生時代にちょこっと研究したことがあります > 適当な名前を考えてくれるエージェント
#既存の名前の構造を分析して
#自然言語→名前の翻訳をする、みたいな分野限定の機械翻訳みたいなことをすればいいのかなあと
#理屈では思ったんですが、作ろうとするとむずかしかった