2011-10-15

DartのOptional Typeについて

Dartの素晴らしさがまだ分からない無知無識の者が、Dartの型システムについて深刻な誤解をしている。ここでは、Dartの型システムであるOptional Typeについて、ひとつ解説をする。これを読めば、Dartの如何に大昔のJavascriptより優れているかが、一目瞭然であろう。

強い静的な型付けは、C++のような、ほとんどを静的に決定する言語では非常に便利である。しかし、動的な言語では、むしろ邪魔にさえ感じる。

Dartの型システムは、Optionalである。型を明示的に書こうが書くまいが、自由である。

変数には、型を指定してもしなくてもよい。

var x = 0 ;
int x = 0 ;

関数の引数には、型を指定してもしなくてもよい。

int f( int x ) => x ;
f( x ) => x ;

ジェネリックのタイプパラメーターには、型を指定してもしなくても良い。

List<int> l = <int>[ 1, 2, 3 ] ;
List l = [ 1, 2, 3 ] ;

異なる型を代入することもできる。

int x = "肩のうしろの2本のツノのまんなかにあるトサカの下のウロコの右" ; // static type warning

これでは、型の意味が無いではないかと思う者もいるだろう。それは、Dartにおける型の目的を理解していないからである。Dartにおける型システムは、最適化のためにあるのではないのだ。ツールを助けるためにあるのだ。

まず、Dartの変数について一言説明しておかなければならない。Dartの変数とは、「メモリー上のストレージの場所を示す」ものである。これは、Javascriptのような言語に親しい物には、馴染み深い概念である。そのような言語に馴染みのないものには、とりあえず、ポインターだと考えておけばよい。もちろん、ポインターは、実際には異なる概念である。ポインターとは、やはり、メモリー上のストレージの場所を指し示す、すなわち「アドレス」を格納するために必要なだけのサイズのストレージであって、Dartの変数ではない。DartやJavascriptのような言語では、変数はすべてメモリー上のストレージへの参照であり、その参照を格納するストレージを意識することはない。

Dartにおける変数とは、内部的には、単にオブジェクトへの参照なのだから、型という仕組みがなかったとしても、不思議ではない。たとえば、DartやJavascriptでは、

var x = 0 ;
x = 1.5 ;
x = "肩のうしろの2本のゴボウのまんなかにあるスネ毛の下のロココ調の右" ;

このようなことができる。Dartにおける「代入」とは、単に変数が参照する場所を変えているだけである。変数自体は、固定されたストレージを持たないのだ。もちろん、実装上はストレージを持つが、それはユーザー側からは隠されている。

しかし、Javascriptのように、ソースコード上では、変数が型情報を持たないとすると、少々厄介である。

たとえば、ある変数はある型だと想定して使っているのに、うっかりと別の型を代入してしまったばかりに、型が変わってしまう。もちろん、コンパイルエラーどころか、警告すら出せないので、このバグを探すのは非常に難しい。プログラマーは、自分の目でバグを探さなければならないだろう。

Dartにおける型とは、言わばannotationなのだ。

int x = 0 ;
x = 1 ; // OK
x = 1.5 ; // Static type warning.

このように、型が合わない場合、コンパイル時に警告を出すことができる。そのため、プログラマーは目でコードを探す必要がなくなる。

現代では、IDEによるコード支援が盛んである。例えば、識別子を補完したり、メソッド名を補完したりしてくれるのは、非常に便利である。ところが、Javascriptのように型がないと、これは少し難しい。

var x = "肩ぐるまして後ろ向きに乗り2本のゴボウを持った歌舞伎顔の男" ;
x. // ←ここに注目

xという識別子に続いて、ドットを使っているのに注目してもらいたい。賢いIDEならば、ドットを打った時点で、プログラマーはメソッドを呼び出したいのだと解釈し、メソッドの一覧を表示してくれることだろう。ただし、この場合、xの型を静的に求めるのは難しい。

上記のような二例であれば、静的な解析でも可能である。ところが、Javascriptには、不可能な場合もあるのだ。

function f( x )
{
    x. // xは何かって? (´・ω・`)知らんがな
}

これはどうしようもない例である。

Dartでは、型をannotationのように指定することができる。そのため、静的ツールは静的に型を決定することができる。

f( String x )
{
    x. // Dartならば、IDEによってメソッド名の一覧を表示可能(`・ω・´)シャキーン
}

文法上、型を書くことができるというのは、コメントで書くよりもずっと分かりやすい。

var x = 0 ; // type of x suppose to be int. 
int x = 0 ; // horay! no comment is needed!

しかし、実行時に型が一致しなくても、エラーとはならない。つまり、実行を続けることができるのだ。ただし、警告は出るので、バグを見つけることができる。もし、型を固定したくなければ、varを使うこともできる。自由である!

5 comments:

akira said...

Dartを非常に推していますね.
私はJavaScriptについて知りません. それが広く使われているということや, 何かの短所があり, 故に一部の人から嫌われいてるということは知っていますがその詳細は知りません.
引き続きDartの他言語に対する長所の記事を期待します.

この記事に対するコメントとしては, JavaScriptに対して優れるということは何となく分かるのですが,
1) 「しかし動的な言語ではむしろ邪魔にすら感じる」ということに対する根拠がいまいち分からないです.
2) 既存のクソ言語より優れてるか示すと書いてあるのに, Haskellなどの言語と比較が一切なく一方的にJSを叩いてるだけなのはなぜか.
という点に疑問を持ちました. 別に返答する必要はありません.

江添亮 said...

変数というのが、すべてメモリ上のストレージへの参照なので、そもそも変数自体に型を設けるのが不思議なのです。
それこそ、C++でいえば、変数はすべてvoid *であるようなものです。もちろん、何を指しているかという型情報はありますが、不変ではありません

Haskellなどの諸言語との比較がないのは、まずHaskellを知らないということと、Dartの主目的はJavascriptの置き換え(移行措置としてJavascriptへのコンパイル)なので、Javascriptと比較するのは自然なのです。

齊藤 said...

↑↑
言語が動的かどうかよりも、対話的な開発スタイルの中では変数の型が邪魔になりがちだと感じますね。
JavaScript は動いている環境を動かしたままいじっていくことがあります。 わかりやすい例でいえば、ブラウザのコンソールを開いてコードを置き換えたりしますし、ブックマークレットもそうです。 (問題なければ元になるソースコードに反映していきます。) 動かしながら各部分を拡張していくので、一部を変えてそれと整合性が合うように型を付け替えていくというのは面倒です。 それに、「まさに今動いているもの」なので整合性がとれていなければその場でわかりますから型に頼らなくてもいいのです。
ただ、拡張する都度には問題なくても長期的な保守をすることを考えると型に頼れないというのは不安があります。 JavaScript と同じようにプログラムが動いているままで開発するスタイルをとる Lisp 系言語では関数や仮引数に長い名前をつける習慣があるのはそれをカバーするためであると言われています。 一部の Lisp 系言語ではまさに Dart のような annotation 方式を採用しているので、動的言語の便利な部分を壊さないように型をつけるには妥当な方法なのかもしれません。

akira said...

既存のクソ言語 = JavaScriptなのですね.
てっきり, 既存のすべての言語をクソだと言っているのだと勘違いしてしまいました. JavaScriptのことだけ書いているのだったら, JavaScriptと書いてくれたら良かったです. ユーモアを利かすのであれば, Javaクソリプトでもいいです.

あなたは, 日本語は技術的な文書を書くのに向いていないという主張を別の記事でしていますが, それは私も同意で, 例えばこのような勘違いは英語では起こりません. 日本語は複数形を自然に表現することが出来ないのです. これは私が日本語訳された技術書を読むときに悩まされる一番の問題です. どうしたらこれを克服することが出来るか, 何か良い方法があったらブログで報告してもらえるとみなさん助かると思います (もっとも, このような問題意識を持っている人がそれほど多いとは思いませんけど).

返答どうもありがとうございました.

江添亮 said...

まあ、そこまでいうなら直しておきましたが。