テーマは「動的型付け言語と大規模開発」

まつもとゆきひろ氏:まつもとゆきひろです。Matzチャンネル17回目ということでお送りします。ちょっと前になりますが9月28日に私が技術顧問を始めたクラウドサーカスという会社さんがテックイベントを開催されて、その時のテーマが「動的型付け言語と大規模開発」というテーマでした。

その時に話したこととか、話そうとしたこと、話そうと思っていたんだけど時間の関係で話せなかったことなどを補足する意味も含めて今日はちょっと放送しようかと思います。というか、分量が多いので2回に分けて話そうかなと思っています。

このクラウドサーカスのイベントのテーマは別に私から指定したわけではなくて、先方が「こんなテーマで話したいんだ」とか「聞きたいんですけど」ということで設定されたんですね。

「動的型付け言語と大規模開発」。例えばRubyとか、Ruby on Railsとかで開発をしていると規模がだんだん大きくなることが多いわけですよね。そうすると、「静的な型宣言のない言語で開発して大丈夫か?」みたいなことを言われたりするわけで、そのへんの不安が関係してこのテーマが決められたと思うんですけれども。

この「不安」というのがわかりますね。特に最近だと、ポピュラーな多くの言語は静的な型宣言を持っているわけですよね。新しめの言語だとRustやGoもそうですよね。Swift、Kotlin、ちょっと前だとJava。JavaScriptには型宣言がないんだけど、最近はJavaScriptに型宣言の付いたTypeScriptのほうが人気だったりします。

それから昔からある言語の中でPython 3やPHPは言語仕様の中に型宣言が入ってきた。昔はなかったものが入ってきたという感じで、そういう言語のほうが有名で、使っている人も多かったりすると、Lisp以来50年の歴史を誇る動的型言語、あるいは動的型付けの言語。Lispであるとか、古いPythonであるとか、それからRubyもそうですけれども、そういう言語を使っているとちょっと流行遅れとか、「心理的安全性に不安が……」みたいなことがあるかなと思います。

なので前半は、動的型付け言語というのはどういうポジションで、どういうものか。あるいは、静的型付け言語にはどういうメリット・デメリットがあるかについてちょっとお話ししようかなと思っています。では、次のチャプターに進みましょう。

静的な型宣言があることの4つのメリット

いろいろなプログラミング言語がありますけれども、ある側面で大きく分けると静的な型宣言を持っているプログラミング言語と、そうでないプログラミング言語。静的な型宣言を持たない言語があって、最初のほう(静的な型宣言を持っている言語)としてはRustやGoがあって、型宣言を持たない言語としてはRubyとかがある。Lispもそうですね。

じゃあ静的な型宣言があることのメリットとデメリットについて、ちょっと語りたいと思うんですけれども。メリットとしていろいろなことが言われるんですけど、1つはですね、やはり型チェックが行われるのでコンパイル時、あるいはプログラムをスキャンする時に、実行に寄らずに型チェックが行われて、型の不整合があると自動的に検出される、網羅的に自動的に検出されるというのがメリットであると言われることが多いですね。確かにそのとおりで、型宣言のない言語だと実際に実行してみないと機械的なエラーが発生しないので、そういう意味でいうと型チェックがあって網羅的に行うのは非常に安心感があるというのは確かなんですよね。

かつ、ロジックのエラーがある時は、その型の不整合が伴うことが割合としてはかなり高い。「ここの型の不整合がありました。これを直すことによって問題解決します。ロジックの間違いが直せます」というようなことがけっこうあるので、その安心感。

それから2点目としてはですね、型宣言の場合特に引数の型や戻り値の型は、ドキュメントとしての性質を持つということが言えると思います。もちろん動的な型言語でも、このメソッドは引数をいくつ取ります、整数を取ります、文字列を取りますみたいなことをドキュメントで書くわけです。

けれども、ドキュメントってあくまでもコメントだったり、別の文書だったりするので、例えばプログラムのアップデートが行われた時に、ドキュメントも同時にアップデートしないと不整合が起きるわけです。ですが、型宣言はプログラムの一部で、そこで不整合が起きると型エラーが起きちゃうので、不整合が起きないドキュメントとしての役割というのがある。

なので、例えばHaskellみたいな型推論ができます、書かなくてもプログラムが動きますというような言語でも、引数で戻り値の型だけは書く人たちがけっこう多いんですね。これはドキュメントとしての役割を期待しているわけです。

さらに型情報が多いので、3番目のメリットとして、型宣言の情報がプログラムなりに書いてあったり、その情報を使って例えば「VSCode」みたいなIDE、統合開発環境の中で補完をしたり、あるいはより多くのチェックをしたりできるのがメリットとして言われています。

それに関連して4番目ですが、型情報があるので、その型情報をベースにより大胆な最適化を行うことによってパフォーマンスが向上するというメリットがあると言われています。

なので静的型付けのメリットは、繰り返しになりますが、より網羅的な型チェックが行われるので安心感が高い。型宣言が、不整合のないドキュメントとしての役割を果たす。3番目が型情報を使った開発の支援。ツールなどが、より強力な支援をしてくれる。4番目、型情報を使ってパフォーマンスの向上ができるというのがメリットになります。

静的な型宣言があることデメリットは煩雑さと複雑さ

一方でデメリットがないかというと、何もかもがトレードオフなのでいいことばかりではありません。例えば、Rubyの型宣言はそもそもないので、プログラムは型宣言がなくても動作するわけですよね。

つまり、型宣言というのはDRYプリンシプルですかね。「重複を避ける」というのは原則的にはいらないというか、便利な点があっても、なくても動くものなんですよね。そういう意味でいうと、型宣言をわざわざ書かなくても……(書くのは)煩雑であるというのはあります。「怠け者」と言われたらそのとおりなんですけれども。

もう1つは、型宣言ってプログラムが動くもののわりには複雑なんですね。例えば、TypeScriptの型宣言。これは整数ですとか、これ文字列ですとか言っているあたりは簡単なんです。

けれども、例えばジェネリック。複数の型をまとめて扱うことができる「パラメタライズド型」みたいな言い方もしますが、ジェネリックを含めると非常に複雑です。ある論文によると、TypeScriptを始めとする静的型言語の型宣言は、それそのものがチューリング完全であると。つまり、任意のアルゴリズムを型宣言だけで、コンパイル時に記述可能であると言われています。

実際に、確かTypeScriptで三目並べを型チェックだけで書くことができる。何を言っているかわからないと思うんですけど(笑)。IDEの中で型を書くと、型の中でその三目並べが成立しているか・していないかとか、あるいはどっちが勝ったとか、負けたとかを判定してくれるアルゴリズムを型宣言のみで書くというのがブログで紹介されていたりとかするので、なくてもいいもののわりにはえらい複雑なんですよね。

それを考えてみると、この複雑さ……。すごくシンプルなうちはうれしいんだけど。しかも、きちんと健全な型チェックを行うためにはそのジェネリックとかが必要なんですけれども、それを突き詰めていくとチューリング完全になってしまうというのが、ちょっとやりすぎじゃないかと思うところがあるわけですね。それが静的型チェックのデメリットと言ってもいいんじゃないかな。

静的型チェックがあった時のうれしさと、型宣言を書かなくても済むというのが両立したらしたでいいんじゃないかなと感じたりするわけです。

静的型宣言のメリットを繰り返すと、型チェックによる安心感、不整合のないドキュメントとしての役割、それからIDEのようなツールが型情報により強力な支援ができる。そして、そのパフォーマンスが高い可能性があるという4つのメリットが言われていましたが、じゃあ、この4つのメリットが型宣言がないと実現できないかというと、必ずしもそうではないところがあるわけです。

型宣言がなくても静的型宣言のメリットは実現できる

例えば、Ruby3.0から静的型チェックを導入するようになったんですけれども、型宣言はなかったんですね。つまり、型情報はそのプログラムとは別のところに持っていて、その情報を使って型チェックを行おうと。ないものについては推論をしようというアプローチですね。

もちろん、今のRubyの静的型チェックは誕生したばかりでまだ未成熟なんですけれども、原理的には、型宣言なしで型チェックの恩恵をある程度受けることができると言われています。がんばっている途中ですね。ただ、型宣言はないので、ドキュメントとしての役割は果たせません。

ただ、そうはいっても型情報はいろいろあるので、そこによる推論結果みたいなものをIDEで表示することによって、ドキュメントとしての役割をある程度果たせますし、Rubyには、インラインドキュメント。つまり型がなくてもいずれにしても振る舞いは書かないといけないのでインラインドキュメントをする習慣があって、型情報を書いたらそれを取り込んでくる。型情報として取り込んでくるというツールも今開発中です。

型情報は、インラインドキュメントから取り込んできたり、外部ファイルに持つことによって型情報を持つと、IDEなどはその情報を使うことができるので、型宣言のメリットであるIDEによるツールの支援に頼ることが可能になります。

さらにパフォーマンスの向上の面でも、例えば「Chrome」に入っているV8というコンパイラはJavaScriptのバーチャルマシンなので、もともと型情報はないんですよね。

なんだけど、実行時の型情報みたいなものを活用することによって、下手すると型宣言によって与えられた情報よりも強力な最適化をする余地があるんですね。実際にV8は、バーチャルマシンとしてJavaScriptの実行が非常に高速にできています。そのことを考えると、パフォーマンスについても型宣言は実は必須ではないということです。

さらに、先ほど、「ロジックのエラーが発生する時に型エラーを伴うことが多い」と言ったんですけれども。ただ、型の不整合が起きなくてもロジックのエラーが起きることは当然あるわけですね。それは無視できるほど少ないかというと、そんなことはない。頻繁にあるわけです。そうすると、結局ロジックをテストすることが必要なんですね。

きちんとテストすることによって、実は型宣言をしたのと同じ程度のエラーのチェックは、型の不整合の存在も含めて検出されるわけです。あまりドライじゃないので、本当は私もテストを書きたくないんですけど、人類はまだテストを不要とする技術を開発していないので、テストはいるんですね。

テストがないと安心してリファクタリングもできずに、結局、技術的負債が増えるみたいなことが起きる。そうすると、型宣言というのは言われるほど、必須なものではない。とすると、今非常にポピュラーな静的な型宣言がある言語は、もちろん実利もあるんですけれど、それ以上に流行によって、2010年代から2020年代現在にかけては、静的な型宣言がある言語が非常に人気があるということなんだと思うんですね。言語デザイナーとしては、そこが悩ましいところで、例えばPythonとか、PHPがやったみたいに型宣言を入れて漸近的型宣言というんですかね? 宣言するところは宣言して、しないところはしないままで動かすよみたいなアプローチが十分にありえると思うんです。

Rubyに型宣言を入れない理由

ただ、一度言語に型宣言を持たせると、もう取り除けないわけですよ。型宣言のあるプログラムがもうそこにたくさんできちゃうわけです。さらに、コミュニティの中におそらくですが、型警察みたいなものが出てくると思うんですね。

ジェムとかライブラリを作っている人のところに行って、「お前の使っているこのライブラリに型宣言がないんだけど、型宣言は絶対にあるべきだ」みたいに言って、型宣言を付けていないプログラムに対して文句をつけまくるみたいな人たちがコミュニティに登場しそうな気がするんですね。実際によその言語ではそういうことが発生していると聞くんですね。

たぶんコミュニティの雰囲気がすごく悪くなると思っていて、そういうのもあって、「Rubyに型宣言を入れるか」と言われた時に、入れたくないというのが正直なところです。さらに言うと、流行というのは振り子みたいなものなんですね。型宣言のある言語が流行ったり、ない言語が流行ったりというのが、ここ何十年もずっと交代に起きてきました。

なので、例えばこれから10年後、あるいは15年後、20年後に、型宣言がない言語が流行る可能性はけっこう高いんじゃないかなと思うんですよ。もちろん、昔のRubyや昔のLispみたいに「型宣言なんかありません。必ず動的チェックをします」みたいなものではなくて、ある種の静的型チェックが導入されて、だけど型宣言のない言語が未来で流行るんじゃないかなと私は思っています。

長い目で見ると、その時に今のRubyに型宣言を入れちゃうと、その時の新しい言語の仲間にRubyを入れてもらえないみたいなことが起きるんじゃないかなと思っています。

今はね、型宣言がないために難しいこと。例えばRBS、Ruby Signatureという、別ファイルをメンテナンスしないといけないという、面倒くさいことをしているんですけれども。でも10年、15年、20年先の未来で十分にテクノロジーが進歩した暁には、十分にコンパイラが賢くなって、型宣言がなくても幸せな開発ができる未来が来るんじゃないかなというふうに思っていて、そのためには、今のRubyに型宣言を入れるのは賢い選択ではないんじゃないかなと思っているんですね。

未来はわからないので、この私のこだわりや判断が、未来の人から笑われる可能性は十分に高いんですが(笑)。でもRubyは一応私の言語なので、今は自分の未来予測を信じて、ちょっとここは意地を張ってみようかなと考えています。

じゃあ17回目は、ここで終わろうと思います。次回は後半ですね。「大規模開発とRuby」というテーマでちょっと話をしようと思います。それではまた。