#んー、CやC++の仕様にも未定義 (処理系依存) 部分ってあるのだけれど RT: @cametan_001 ぶっちゃけ、Schemeの「バグ報告」に関してはやりづらい。っつーかやりたくない(笑)。一応他の処理系の動作と比較して「バグなんじゃない?」とか思うんだけど、ところがどっこい、「実装側の解釈」が仕様で成り立ってるんで、これはアテに出来ないんだよ。
#最近見たのは何だったかな。 do { /*何もしない*/ } while (副作用のない、複雑な条件判断); っていうループをCコンパイラは削除できるかどうかって論争。
#whileの条件のところに、例えば「ゴールドバッハ予想が真なら成立」という条件を書いといて、そのループを抜けるコードが生成されたら、コンパイラはゴールドバッハ予想が偽であると判断したことになるんじゃない? とかそういう話。
#これは、「停止するかしないか」という問題はコンパイラが考慮すべき副作用にはあたらない、って結論だったようだけれど。
#この辺面白い。 RT: shiro: 最近見たのは何だったかな。 do { /*何もしない*/ } while (副作用のない、複雑な条件判断); っていうループをCコンパイラは削除できるか… http://bit.ly/aTQyHk (via ) #えぇっ?コンパイラはそんな判断をして脱出コードを吐くわけではないですよね。
それとも最適化ロジックとして脱出不要と判断したら脱出コードを生成しないとかそういう前提があるのかな?
まぁ自明でない場合は最適化されずに生成しとけってなりそうだけど。
#上の話だとちょっと変か。実際の議論の発端はフェルマーの定理だったかな。{ int n=0; while (x^n + y^n = z^n なる(xyz)が存在しない) { n++ } } みたいな感じ。これをコンパイルして実行して終了したら、反例が見つかったってこと? っとかそんなようなものだった。
#あ、もっと巧妙だったか。オーバーフローが発生しないような工夫とかもされてた。
#そんで、この場合、停止問題を除けば条件節の成否は外から観察可能な副作用を持たないので、コンパイラはループを消しちゃっていいってことだったと思う。
#もちろんvolatile int n; にすればループは生成されるし、「反例が見つかったnをprintしよう」とかいうコードを付け足せばやっぱりループは生成される。外から観察可能になるからね。
#n=0じゃだめじゃん。n=3からスタートだ。
#マクロとか構文キーワードってのはコンパイラが直接認識するものなので、実行時に値を持たなくてもいいってことですね RT: @cametan_001 Gaucheの場合、例えば(procedure? cond)だと#fを返す。これは全く「その通りだよな」という結果で。condは手続きではないので、#fを返すのは妥当だと思った。ところが、同じ式をPLTで実行するとエラー扱いになる。
#これは確かに混乱の元かもなあ。Scheme界には「展開/コンパイル時と実行時は全く別の世界であるべき」という秩序派と「全部いっしょくたなのがいいところなんじゃないか」という混沌派がいて永遠の昔より争っているので。
#そう。僕は別に黒板の人派じゃないんで(笑)。ただ、コンパイラが…ってのは多分この場合は成り立たないのでは、と。直感的な話すると、GaucheとかGuileの「動作」のほうが納得しやすいんですよ。PLTはそうじゃない。
#あなたのアライメントはchaosですね。> GaucheとかGuileの「動作」のほうが納得しやすいんですよ。
#私もそうなんですが、多分「完全分離の方が理解しやすい」と考えてる人たちもいます。
#s/アライメント/アラインメント/
#そうですね。多分chaos。
#分離・カオス派の区別とは別に cond の構文に沿ってないからエラーってことで納得いきませんかね?
#うーん、少なくともR5RSまでは、condの構文と言った場合はあくまで「リストの第一要素が識別子cond」であるケースについての議論になるでしょう。
#リストの第一要素以外については、あくまで構文としては <variable> であって、condとう識別子はvariableではないからその構文にそっていない、とは言えると思うけれど、「condの構文」とは言えないんじゃないかと。
#R6RSだとidentifier macroがあるから、それと同じ解釈をして、「condが単独で現れた場合の構文は定義されていない」という意味で「condの構文に沿っていない」と言えるかもしれませんが。
#なるほど。 「『cond の』構文」以前の話なわけですね。
#ああ、ぼくも「cond の構文」としてエラーなんだから、エラーにしてくれたほうが親切だと思ってました。
#R5RSを読んだ限りでは、condが単独で現れた場合はエラーにする方が自然だと思います。でも、実装する場合はエラーにしない方が自然な気も。
#いや、表面的な構文だけではrejectできないんですよ。
#(foo cond a b c d) という式を見た場合、fooが実はマクロで、(cond (a b) (c d)) に展開されるという可能性がある。
#なのでマクロ展開の順番を仕様で指定しないとならないんだけどR5RSではそこまで踏み込んでない。と書こうとしたけど違うか。評価までには展開されてるのは確実だからこの議論は成り立たないな。取り消し。
#R5RSの解釈に関しては、syntactic keywordがリストの先頭以外に出てきた場合について「何も言っていない」ので、どちらが自然とは言えないと思います。
#http://twitter.com/cametan_001/status/14744189207 Schemeの仕様を読むのに実装者視点が要るってのには半分同意するけど (マクロを書くって半分言語実装に足突っ込んでることになるから)、その後の末尾再帰の例は同意できないな。末尾再帰でループ書けるじゃん、てのはプログラマ視点ですよ。 #関数型思考ではあるけれど。末尾再帰がどう実現されてるかは気にする必要ない。実際、ジャンプに変換する必要はない。リソースを消費しなければいいので、例えばスタックをポップせずにどんどん呼び出していって後でスタックをガベージコレクトしてもいい。
#それこそどう実現するかは実装者が気にすべきことで、プログラマ視点としては「安全に(リソース消費せずに)末尾呼び出しが書けるかどうか」という一点だけが知りたい。それがあるかないかでプログラミングスタイルががらりと変わってくるわけだから。
#最近 OCaml のリファレンスマニュアルを読んだんですけど、 RnRS が頭にあると、これを読んでも OCaml 言語の実装はできそうにないなあと思いました http://caml.inria.fr/pub/docs/manual-ocaml/language.html #でも、これは、このドキュメントが「リファレンスマニュアル」であって、「言語仕様」ではないかただとも思いました
#s/ないかた/ないから/
#実際、言語仕様ってユーザーが読むものではなくて、実装者だけが読めばいいものなんじゃないかなあと思います
#(ANSI CL の spec がどんな感じかは知らない)
#CommonLispの仕様の方がコンパイラ実装者よりのような気がする。
#いまさらながら、最近Lispコンパイラ作っていて思うのが、仕様の大きさはともかくとして、SchemeよりCommonLispの仕様の方がコンパイラ実装時には自然な気がする。
#ってある意味当たり前か。
#言語仕様って、ユーザも読むんじゃないかな。
#挙動が怪しいときに、言語処理系のバグなのか仕様なのかの切り分けが必要になるときってありますし。
#C言語とかポータビリティを考え始めると仕様を読まないと怖いんじゃないかと。
#そうですね。移植性を考え始めると言語仕様を読まないと怖い
#そういえば、いつだかの shibuya.lisp で、 Common Lisp は効率のよい実装ができるような仕様になっている、という意見を聞いたような
#Scheme は R6RS の Introduction にもあるように、最小限の規則で実用的な言語を作ろうとしている
#Scheme は実装よりも理想に寄っている感じはします
#なんとなく Schemeって仕様が、いまだにこなれていない感じがする。Common Lisp より歴史は長いはずなんだけど。
#まあ、Common Lisp はそれ以前の Lisp Machine や MacLisp とかを受け継いだものだから、もっと歴史は長いか。
#まだまだ実験中な感じはありますね
#R6RS のライブラリと phase のあたりは、まだ Right thing がわからないので実装依存の部分を多くした、というような感じがしました
#30年以上たっても、まだ実験中というのがなんか面白い。
#言語仕様書にどこまで書くかは想定ユーザ次第では? Ocamlは研究者が多そう。CLは既存の昔は多数の実装の調停が求められた。可搬性重視のJavaは、組み込みリテラルのハッシュ値も決まってる等々。許容範囲なら抽象度が高いほど実装の自由度は増えるような。
#s/既存の昔は/昔は既存の/ でした
#Lisp 系はみんなが処理系を作る前提で仕様を書いているような感じはします
#そうですね。何しろ構文木を直接書く人達なので。僕もか >Lisp 系はみんなが処理系を作る前提で仕様を書いているような感じはします
#Schemeの仕様は、効率的な実装のしやすさよりは綺麗さを選ぶ傾向がありますね。R6RSのequal?の仕様とか (循環構造を検出しなくちゃならない)。
#末尾呼び出し最適化(TCO)があるかどうかっていうのは、オブジェクトの管理が完全なGCかナイーブなリファレンスカウントか、みたいな差。ナイーブなリファレンスカウントだとプログラマは循環構造つくらないように気を使ったりするでしょ。
#リファレンスカウントでもメモリ管理が無いよりはましだし、「リファレンスが落ちたら即座に開放されるから、いつ開放されるかわからない他のGCよりもいい。循環構造にさえ気をつけてれば」と主張する人もいる。でも循環をつくらないように気をつけることで、プログラミングスタイルは確かに影響される。
#個人のスタイルだけじゃなくて、ライブラリのAPIとかのデザインレベルまで。
#で、完全なGCに慣れた人から見ると、いちいち循環構造作らないように気をつけるなんてめんどくさい、と感じられる。
#TCOに慣れていると (Schemeプログラマだけでなく、関数型から入った人はみんなそうだ)、末尾再帰しててスタックオーバフローというのは、自分の思考が処理系の都合で制限されている、と感じられるわけだね。
#@cametan_001 の言う「一般のプログラマ」というのが人数比での話なら、確かに関数型なプログラマは一般的ではないだろうけれど。
#言語仕様は必ずしも全部は把握してないにしても、いつでも参照できるようになってないと、不安でコードかけないですねぇ。
#ワシはC++の言語仕様を把握できる自信が無いよ…