見出し画像

Retro-gaming and so on

RE: プログラミング学習日記 2022/09/29〜

星田さんの記事に対するコメント。
今回は先にコッチの方から。



 とりあえずサンプルを入れてみて・・なんか()が多くない?


 でも減らすとエラー。どうも((a b) (values ...)) ((c d)(values ...))と、複数束縛出来るようになってるからか? ま、そういうもんだと覚えておこう

そう、let-valuesletより括弧が多い(ように見える)。理由は星田さんが考えてる通り、複数束縛の為、だ。
しかし、そもそも元来のlet自体が括弧がやたら多く、初見殺しだ(※1)。
そして、ポール・グレアムのようなLispハッカーでさえ、「letは括弧が多すぎる」と考えてるようだ。
実際、ポール・グレアムは自身の言語、Arcを作る際にLispプログラマにサーヴェイしてるが、letに関しては大方「構文的には"正しい"とは思うけど、括弧が多すぎる」と言う意見を言われたらしい(※2)。
そこで、Arcをデザインする際に、括弧を減らす工夫をしてる。

ポール・グレアムが言う通り、letなんかは実用上、単項の束縛の方が多い。
let-valuesも、このケースのように単項束縛が目的なら専用のマクロを作っちゃって凌ぐ、ってのも1つのやり方だ。と言うかLispプログラマらしいやり方になるんじゃないか。
例えば次のようなマクロを書く。

(define-syntax arc-let-values
 (syntax-rules ()
  ((_ (var ... val) body ...)
  (let-values (((var ...) val)) 
   body ...))))

そうすれば、括弧を減らした同じ作用を持った新種のlet-valuesになる。


どっちにせよ、Lispに於いてはある構文が気に喰わなかったら新しくマクロとして定義し直しちゃって良い
それをしてもいい、ってのがLispの自由度なんだ(※3)。

とまずはlet-valuesの話を書いておいて。
まず、これは言っておこう。星田さんはセンスがある。ここで多値を思いつくのは、それが動こうが動かまいが、凄く良い事なの。
むしろ、機能を学んでも思いつかない、って人の方が多いんだよ(笑)。
つまり、星田さんの抱えてるツールの種類は増えてる一方なんだ。これは喜ばしい事だと言える。

で、だ。


 saitan_wo_bunriで単発実験。あれ?

 どうも多値で返してない模様・・・。変数を単品にするとリストで入ってる。Ocamlだと多値として返されるのかな?

非常に面白い実験を行ってる。
結論から言うと、

  1. let-valuesは多値関数からの返り値を受け取る為の束縛機構だ。
  2. let-valuesは実はlet上位互換の機能だ。
これらがポイントとなる。
1. が言ってるのは、saitan_wo_bunri関数は全然多値関数として設計されてなかった、と言う事だ。返り値は単なるリストだった筈だ。これは多値ではない(言い換えると、返り値がvaluesを使ってなかった・・・使うように改造してもいいけど)。
2. が言ってるのは、let-valuesは多値関数の返り値を束縛する為に作られた機能なんだけど、面白い事にフツーのletとしても使えるんだ。
つまり、星田さんの二段目の実験は、

(let ((saitan (saitan_wo_bunriCFL test-eki-list)))
 (display saitan))

と書いてるのと事実上同じだ、って事だ。

実はこの関数、dijkstra_mainの場合、let-valuesの使用は結果適さない。
じゃあどうすればいいんだろ?と言うと、match-lambda*のせいで脳がスッカリそっち行っちゃったんだけど、ここで使用すべきものこそが、match-letだ。
OCamlでのコードを直訳するなら、恐らく次のようになるだろう。

(define (dijkstra-main eki-list ekikan-list)
 (match eki-list
  (() ())
  ((first . rest)
   (match-let (((saitan . nokori) (saitan-wo-bunri (cons first rest)))) ;; ここ
    (let ((eki-list2 (koushin saitan nokori ekikan-list)))
     (cons saitan (dijkstra-main eki-list2 ekikan-list)))))))

※1: letcondの括弧の多さは異常と言ってよく、Lispを学びにやってきたNew Comersを怯えさせるには充分強力だ。Lispの「括弧だらけの言語」と言う悪評は、この2つが作り上げてる、って断罪しても良いくらいだと思う。

※2: ここで言う「正しい」と言うのは、コンピュータサイエンス的で、要は「汎用であらゆるケースに対応出来る」と言うような意味だ。
ただし、「正しさ」を中心にデザインすれば、何故か「使いづらい」とか「見づらい」とかがしばしば出てくるのが不思議なトコだ。

※3: マクロはドメイン特化言語(DSL)の為のツール、だと良く言われる。
ここで、ドメイン、ってのは原義的には色んな意味に取れるが、極論、「今自分が書いてる1つのプログラム」と言う意味で捉える事が出来る。
いや、ハッキリ言っちゃえば、マクロに限らず、「プログラムとして関数を書く」と言うのはドメイン特化関数を書いてる、って事と同じだ。プログラミング言語が用意してる汎用関数「だけ」でプログラムを作ろう、なんつー奇特なヤツはいないだろう。そんな事をしたらソースコードは、動くにせよメチャクチャなモノとなってしまう。
何が言いたいか、と言うと、「マクロを書く」場合、望ましいのは確かに「汎用として使えるユーティリティになればいいな」とは言えるけど、現実問題として、別に「あるプログラムに最適化した」汎用じゃないマクロを書いても構わない、と言う事だ。言い換えると「他のプログラムでは使いようがない」マクロを書いてもO.K.だ、と言うこと。
これを否定するとそもそもDSLと言うアイディア自体が馬鹿げたものになってしまう。
関数もマクロもそうだが、「汎用ユーティリティになりそうだ」と言うのは別のプログラムを書いた時に「同じ関数やマクロ」が出てきた時だ。「二度ある事は三度ある」からね。その時初めて汎用化を考えればいいだけで、実は初回に於いてはそんな事は分からないんだ。
ハッキリ言っちゃって、最初から「汎用的なマクロを作ろう」と言うのは難しいし無理だ。関数でさえ最初っから「汎用的な関数を書こう」ってのが無理、ってのと同じだ。二度、三度と「同じようなパターン」を目にするようになってから「汎用的なユーティリティに育てよう」となるわけだ。
と言うわけで、マクロの設計が難しいのは事実だが、同時に「最初から汎用を目指さない」のなら、特定のケースのみ、バグが起きない程度の「限定した使用目的の」マクロを、取り敢えずは「気楽に」書いてみるのは正しい、と思われる。使い回しをしない、のだったら隠されたバグも表面には出てこないし、まさしくDSL、としての役割を担ってくれるだろう。
また、書いてみなければどんな問題が伏在しているのかも分からない。マクロもまた「やってみなけりゃ分からない」機能なんだ。
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

最近の「RE: プログラミング学習日記」カテゴリーもっと見る

最近の記事
バックナンバー
人気記事