見出し画像

Retro-gaming and so on

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

星田さんの記事に対するコメント。

今回は一つだけ、ある意味フレーム(※1)の元、動的型付け言語 vs. 静的型付け言語だ。

知ってる限り、(往年のBASICを除くが)、星田さんは動的型付け言語の経験しかない。PythonとLispだな。
今回、恐らくはじめて、間接的にOCamlにより、「静的型付け言語」の世界を覗いてる。C言語、C++、JavaもグルーピングとしてはOCamlと同じ「静的型付け言語」に属してる。
関数型言語、オブジェクト指向言語、等のグルーピングより明らかにハッキリした区分けだ。

実は両者とも「型」って言う概念がある(※2)。整数型、浮動小数点型、文字型、文字列型、等の事だ(※3)。
ザックリ言うと、静的型付け言語ってのはプログラマが変数及び関数を扱う際に、明示的に「これからこの型を使います」と宣言しなければならない言語の事。
一方、動的型付け言語は、「実行時に型が決定する」方式。つまり、コンパイラなりインタプリタなりが、プログラムを実行する際に「これはxx型だな」と自動的に判断してくれる。従って、静的型付け言語のようにプログラマが変数及び関数を扱う際に、わざわざ型を記述する必要がない(※4)。

と言うのが一応、教科書的な静的型付け言語と動的型付け言語の説明だ。
ところで、Lispで次のようなバカみたいな関数を書いてみる。

(define (foo arg)
 arg)

受け取った引数をそのまま返す関数、だよな。



ところがLispではたった二行で書けるおバカ関数でも原理的には静的型付け言語では書けないんだ。何故か?関数の返り値の型も厳密に決めないとならない、からだ。
上の関数fooは、Lispで書かれてる為、どんな型の引数でも受け入れる。当然、返り値もどんな型にもなりうる。
しかし、静的型付け言語の場合、引数の型を指定しなければならない上、返り値の型も決めないとならない。
古典的な静的型付け言語の代表格、C言語だと、上と同種の結果を返すには、(シンボルが無いんで置いておくけど)、低でも4つの関数を別々に作らないとならない

int foo_int(int arg) {
 return arg;
}

double foo_double(double arg) {
 return arg;
}

char* foo_string(char* arg) {
 return arg;
}

char foo_char(char arg) {
 return arg;
}


そして、仮にLispだと、構造体を作って関数fooを適用しても問題なく実行出来る。一方、静的型付け言語だと、原理的には、また新しく、構造体を引数に取って構造体を返す関数を作らなきゃならないんだ(※5)。
静的型付け言語にとっては厄介なのが、これらバラバラに作ったプログラムを原則統合出来ない、と言った辺りだ。
例えばLispなら絶対書かないけど、いずれにせよ、こういったプログラムさえ書けない、ってのが原則だ。

(define (foo arg)
 (cond ((integer? arg) arg)
    ((number? arg) arg)
    ((symbol? arg) arg)
    ((string? arg) arg)
    ((char? arg) arg)
    (else (error "unexpected type: " arg))))

ここでは(Lispでは無駄な)型による振り分けを行っている。
この方式だと静的型付け言語でも出来そうな気がするんだけど、やっぱ出来ない。何故なら、

  1. どっちにせよ、原理的には型をボカシた仮引数を受け取れない。
  2. そしてやっぱり返り値の型を決定出来ない。
Lispが、と言うより動的型付け言語で「当たり前に可能な」プログラミングが静的型付け言語では、原理的にはやっぱり出来ない、って事になる(※6)。
動的型付け言語が静的型付け言語より「自由/ラクに書ける」と言うのが分かるだろう(※7)。

こういう話が動的型付け言語での発想と静的型付け言語での間ではしょっちゅう衝突を起こすんだ。

例えば、だ。
SRIF-1にご存知iota、って関数が提供されている。

(iota 5) => (0 1 2 3 4)
(iota 5 0 -0.1) => (0 -0.1 -0.2 -0.3 -0.4)

見た通り、数字の列を得る事が目的で、どんな数でもお構いなしに生成する。何なら増減指定が複素数型でも構わない。

> (iota 5 1+i)
'(1+1i 2+1i 3+1i 4+1i 5+1i)

これはiotaが「数を生成する」と言うロジックで組まれていて、「どんな数の型なのか」を問わないから、だ。
一方、C++のiotaは違う。そこに書かれている通り、だ。

指定された値から始まる整数列を生成する。

つまり、仮にdouble型(つまり小数点絡みだ)での数値列が欲しい、ってなった場合、iotaは役に立たない。プログラミングする際、そういう数値列が欲しい局面があるだろ、とか思うんだが、生成される返り値が決まってるんで、応用が効かないんだ。しかもリストみたいなコンテナを生成するわけでもないんで、使い勝手はPythonのrangeなんかに比べてもクソだ、って事になる。
一体、何の為のユーティリティなんだ、って思う程だ。

さて、これで大体分かったと思う。例の箇所は「返り値の型が既に決まってる」から返り値の型に合わせたブツを返さなければならない、と言う静的型付け言語特有の縛りが導いたモノだ(※8)。

(* saitan_wo_bunri : eki_t list -> eki_t * eki_t list *)

ここの返り値の型を指示してる、eki_t * eki_t list が成立するように[]のパターンマッチの結果を記述しなきゃならないわけだ。

※1: 炎上、または炎上案件、の事。
実はネット用語の「炎上」は英単語でのネット用語、flameの訳語だ。
これはなかなか翻訳センスがある、と思っている。

※2: 英語ではまんま「type」。動的型付けは英語では「dynamic typing」、静的型付けは「static typing」と呼ぶ。
ちなみに、コンピュータ用語で「静的」「動的」と言うのは英語での「static」「dynamic」の訳語だが、イメージ的に、静的はそのまんま「動かない」、動的は「動く」何か、と捉えがちだが、実はその辺にはあんまり意味がない。と言うかそういうイメージは大体誤解の元だ。
実は、英語でのコンピュータ用語の「static」「dynamic」と言うのは大方のケースでは、二項対立する同フィールドの概念/モノに対して、ラベルとしてそれぞれにstatic、dynamic、を貼り付けてるに過ぎない。
つまり、日本語で言うと、実は「陰陽道」なんかの「陰」と「陽」みたいなモノで、優劣がある、と言うわけじゃない対立した概念/モノに付けるラベルと大して変わらないんだ。
あるいは、negativeとpositive、つまり「ネガ」と「ポジ」みたいなモノだ。または「勇者」と「魔王」とかな(笑)。
ただ、negative、positive、だとどうしてもどっちかが優、どっちかが劣、って印象を持ちやすいんで、代わりにstatic、dynamic、と言ってるに過ぎない。
いずれにせよ、何らかの「アクション的なモノ」と言うニュアンスは元々無いんだ。

※3: 細かく言うと、CとC++は原理的には「型がない」って言って良い程で、C/C++に於いては「型」ってのは何バイトまとめて扱います、と言ったコンパイラへの指令程度の意味しかなく、Javaでの「型」とは様相が違う。
が、ここではその辺の話は置いておく。

※4: ただし、静的型付け言語でも、今後の流行りとしては「型推論機能」と言うモノがガンガン取り入れられていくだろう。原理的には静的型付け言語でも、コンパイラ側が型を自動的に判定してくれるシステムだ。

※5: これを解決しようと出てきたのがジェネリックプログラミングと言う機能あるいは技法だ。
ジェネリックプログラミングは、結果を使う側に取っては負担が減るが、一方、ジェネリックプログラミングを「する」側にとっては、本質的には「型に従ったバラバラのプログラムを書かなければならない」と言う問題は解決していない。

※6: もちろん、静的型付けプログラミング言語によっては、この辺の「不便さ」を解決するような機能を持ったモノも存在する。OCamlでは型変数なるモノが存在する(教科書要チェック)。
ただし、あくまで「あるプログラミング言語の機能」であって、一般論ではないし、一般論で言うのなら「出来ない」ってのがフツーになる。

※7: ただし、これを実現する為に、たった二行のLisp関数でも裏ではやたらめったら「型の整合性を確保する為に」走り回ってる、と言う事だ。
言い換えると、Lispに限らず、動的型付け言語は静的型付け言語より「計算コストがかかる」前提で、またデータ型自体の内情が複雑化しやすい、と言う事になる。
要は、このコストを「然るべきコスト」と捉えるか、あるいは「無駄」と捉えるか、が人によって大きく違う、と言う事だ。
なお、ANSI Common Lispだと型を指定して、まるで静的型付け言語のようにしてコンパイルしたコードを最適化する機能まで提供してる。
ただし、川合史朗さんは、それをやりすぎると見た目も安全性もCで書いたコードくらいに危険になる、とは警告している
ちなみに、そこのページの「Lispには安定して世界標準な言語環境がない? 」に川合史朗さんは「Common LispがANSIという 世界標準になった」と書いてるけど、何度か言ったがこれは完全な勘違いだ。ANSIは世界標準ではない。あくまでアメリカローカル規格でしかない、って事だ。
しばしばLisperは規格に対して勘違いしてる事を言うけど、問題は、「世界標準」を問う以上、ISOやそれに準ずる公式規格としてはどうなのか、と言う話にならざるを得ないんだ。それで言うと「Lispには安定して世界標準な言語環境がない」ってツッコミは正しい。
より正確には「LispにはISLISPと言う世界標準な仕様はあるけど、マトモに使える言語実装/環境がない」と言うので、実は批判は全くその通り、なんだ。
川合史朗さんにしては珍しく間違ったツッコミをしてて、猿も木から落ちる、事もある、って事だろう。

※8: 静的型付け言語側の人々は、静的型付けの方が安全でバグがないソフトウェアを書ける、と主張しているが、翻ると、それは「プログラムを書く側が多大な縛りを受け入れて」と言う条件が付くように見える。少なくとも、動的型付け言語ユーザーにはそういう議論に見えるわけだ。
結果、個人的な資質で、「みんなの為に縛りを受け入れる」性格なのか、あるいは「自分がラクならそっちの方が良い」と言うタイプなのか、に分かれるんじゃないか。
そういう意味で言うと、仕事場で使うプログラミング言語と個人で愛用してる言語が全く違う、ってのもあるあるだ、と言う事だ。
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

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

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