召還されたますた。
@dankogaiさんがブログで参入したら面白そう。 「変数に型がないということの利点について考える - サンプルコードによるPerl入門」 (id:perlcodesample / @perlcodesample2) d.hatena.ne.jp/perlcodesample…
— r-west (@rwest2112) February 28, 2013

言語設計者たちが考えること
Mastermind of Programming
Federico Biancuzzi / Shane Warden
伊藤真浩 / 頃末和義 / 佐藤嘉一 / 鈴木幸敏 / 村上雅章訳
[原著:Masterminds of Programming]
まず、よくある誤解を解いておきたい。
PHPやPerlやRubyやPythonなどのスクリプト言語に対して、変数に型がないということを否定的にとらえる人もいるかと思います
けれども、型がないということは、本当に素晴らしいことです。型がないことによって、たくさんの面倒から解放されるからです。
これらの動的言語には、型がある。もう少し厳密に言うと、値は必ず型を持っている。「変数にはどんな型の値でも代入できる」と「変数に型がない」は意味が異なる。
/* JavaScript */ var any; p( any ); p( any = !any ); p( any = any + 41 ); p( any = 4 * Math.atan2(any, any) ); p( any = 'Pi is ' + any ); p( any = [any] ); p( any = {any:any} );
そもそもHaskellのように変数というものを持たない言語もあることを考えれば、それどころか Brainfuck やLazy-K のように、値に名を付けることができない言語すら存在することを鑑みれば、言語を分類するにあたって考慮すべきなのは、変数ではなく値の型ではないのか。
それでは型の視点から見た動的言語と静的言語の違いは何なのか?
実行時に、値が型を「覚えている」かではないのか。
静的言語では、型は捨て去られ、値だけが残る。そのことは、以下のコードからも窺い知ることができる。
/* works not only C but also C++ */ #include <stdio.h> int main(){ const char *hw[] = {"Hello", "world"}; printf("%s, %s!\n", hw[0], /* this is normal */ 1[hw] /* but why does this work? */ ); return 0; }
なぜコンテナ型ではない1
に添字をつけて、しかもその添字が整数ではなくコンテナ型なのにこれがまかり通ってしまうのか? printf()
が要求しているのは、実はただのポインターであり、しかも実行時にはそれがポインターであるという情報すら失われていて、たまたまメモリー上の位置を示しているただの整数になっているからで、それをコンパイラーが黙認しているからで、しかもそれが仕様だからだ。
これに対し、動的言語の値は、実行時においても自分の型を覚えている。
# ruby any = nil p any p any.class p any = !any p any.class p any = (if any then 1 else 0 end) + 41 p any.class p any = 4 * Math.atan2(any, any) p any.class p any = [any] p any.class p any = {'any'=>any} p any.class
このことは何を意味するか?
実行時に値の型を調べ、それに対応したコードを実行するプログラムが書けるということだ。
たとえば、 JSON.stringify
を再発明することを考えてみよう。
こんな感じに比較的簡単に書ける。型を持たない文字列のエスケープの方が大変なぐらいだ。
これが静的言語だと、そうは問屋が卸さない。
たとえば MessagePack の C++ 実装が「オブジェクト」をどう定義しているかを見てみよう。
msgpack-c/src/msgpack/object.hpp at master ・ msgpack/msgpack-c ・ GitHubstruct object { union union_type { bool boolean; uint64_t u64; int64_t i64; double dec; object_array array; object_map map; object_raw raw; object_raw ref; // obsolete }; type::object_type type; union_type via; /* more lines follow */ };
見ての通り、型に相当する情報を、値として持たせている。つまり実行時に型が必要になるようなプログラムを書く時には、言語ではなく自前で「型を示す値のための場所」を確保せねばならないのだ。これはめんどい。
もちろん動的言語といえど、実際のところは型というメタデータを、属性という名のデータとして持たせている。つまり動的言語における「型付きの値」は、静的言語--によってコンパイルされた実行ファイル--における「裸の値」よりもずっとかさばるということだ。例えば Perl の $scalar
は、静的言語であればたった一バイトで済む[0x00,0xff]な数値でも、64bitアーキテクチャーで24byte、32bitアーキテクチャーでも20byteを費消する。値を背負った分重いのだ。
で、結局どっちが良いかって話だけど、私としてはどちらのメリットも捨てがたいんで、 開発形態によって選べるといいなと思うんだけどね。
さらに踏み込んで、「動的言語で書かれたコードでも静的実行可能なら静的実行しよう」というのが、LuaやJavaScriptの世界で今流行りのJIT。当然静的に実行される際には、不要な型情報は全部引き剥がされる。裏をかえせば、実行時に型情報に依存するコードは最適化しづらいということであり、おかげで「JavaScriptを256倍速く書く方法」のような記事が乱立するのは本末転倒だし。AltJSの乱立に至ってはもう何をいわんや…
動的言語のほとんどが静的言語で実装されているのを見れば、静的言語しかない世界があったとしても、誰かが動的言語を書いてしまうは間違いないし、そしてWebブラウザー上の世界のように(事実上。さらばアプレット;_;)動的言語しかない世界であっても今度は静的言語をその上に書く人々が後を立たないことを見れば、我々がどちらか一方だけで満足できないというのは確かだと実感している。
Dan the Dynamic Blogger
追記: 「Java の getClass() とか、C++ の typeid() とかは?」というツッコミに対する答え。
コンパイラーというのは、ソースコードという(バイト列からなる)定数を、オブジェクトコードという別の定数に写像する関数と見なせる。つまりオブジェクトコードが不変なら、型は値化されていることになる。evalさえなければ、全ての型は定数扱いできる
— Dan Kogai (@dankogai) March 1, 2013
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。