制限事項
ホストとなる言語と実行環境を共有する言語は、多くが外部関数インターフェイス(Foreign Function Interface, FFI)と呼ばれる、ホスト言語の関数を直接呼び出すための仕組みを備えています。例えば、Scala は Java の関数を直接呼ぶことができます。他にも Clojure/Java や Python/C、Haskell/C など同様の例が多くあります。
ElmはJavaScriptに対するFFIの仕組みを提供していません。 好きな時に好きなJavaScriptの関数を呼び出す方法は存在しないのです。この決断によってElmはさまざまな長所を得ましたが、同時に覆すことのできない制約も生まれました。これらを好ましい利点として大いに気に入る人もいるでしょうし、到底受け入れられないという人もいるでしょう。Elmを仕事で使うことを検討しているなら、フラグ、ポートとカスタムエレメンツを使ってあなたの要求を満たせるかどうか、JavaScriptとの相互運用の例を見てみてください。
FFIについて、Elmが他の言語と異なる選択をしたのはなぜでしょうか?
トレードオフ
プログラミング言語の歴史の中でも、ポートは特別な存在です。一般的に、異なる言語を相互運用するやり方として次に紹介するふたつの戦略のどちらかひとつが用いられてきましたが、Elmはどちらも選びませんでした。
- 完全な後方互換性。 例えば C++ は C のスーパーセットであり、TypeScript は JavaScript のスーパーセットです。これはとても寛大なアプローチであり、極めて効果的な手法であることも実証されています。スーパーセットであるということは、誰もがその言語をすでに使っているということです。
- 外部関数インターフェイス(Foreign Function Interface, FFI)。 これはホスト言語の関数を直接呼び出すための仕組みです。例えば、Scala は Java の関数を直接呼ぶことができます。Clojure/Java や Python/C、Haskell/C などの多くの言語が同様のアプローチをとっています。繰り返しになりますが、これもとても効果的であることが実証されています。
これらふたつの手法は、言語に柔軟性を与え、より早く普及させるという点では魅力的ですが、主に次のふたつの理由により、Elm にとってはどちらも理想的とはいえませんでした。
- 安全性の保証の欠如。 Elmのすばらしい点のひとつは、Elmを使っているだけで、ある種の問題については一切頭を悩ませる必要がなくなるということです。予期しない例外が送出されることもなければ、関数が意図せずデータに破壊的変更を加える方法もありません。しかし、もし JavaScript を直接呼び出すことができるとしたら、その安全性はすべて台無しになってしまいます。使っているパッケージが実行時エラーを起こすかもしれませんし、それがいつ起きるのか予測することもできません。Elmが渡したデータをそのパッケージが変更してしまうかもしれません。データが変更されたとして、その変更を検出する必要があるでしょうか? パッケージは副作用を持っているでしょうか? 外部のサーバーへデータを送信することはないでしょうか? パスワードを記録することは? JavaScriptを直接使うときには、これだけのことを気にしなくてはならないのです。多くのユーザーが Elm に特に惹きつけられているのは、このようなことに頭を悩ませる必要がまったくないからなのです。
- パッケージの氾濫。 JavaScriptで利用できるものを直接Elmにも複製したいという要望は極めて強く存在しています。もしElmからJavaScriptの関数を直接呼び出せるようになっていたなら、
elm/html
が導入される前の2年のうちに、間違いなく誰かが jQuery のバインディングを作っていたことでしょう。Elmと同じようにJavaScriptへコンパイルされる静的型付け関数型言語でも、より伝統的な相互運用の仕組みを採用している言語では、すでにパッケージの氾濫が起こっています。私が知る限り、パッケージの氾濫は JavaScript へコンパイルされる言語に特有の現象です。他の言語、たとえば Python ではパッケージへの要求はこれほど強くありません。私が思うに、これはJavaScript の独特な文化やエコシステムの歴史がもたらした欠点ではないでしょうか。
これらの落とし穴を考えると、Elm の良い部分を損なわずに、必要であれば JavaScript を使って問題を解決することのできるポートとカスタムエレメンツは、とても魅力的ではないかと思います。素晴らしいですね! 一方で、Elm は JavaScript のエコシステムに乗っかって迅速にライブラリを増やすことはできません。それでもなお、長い目で見るとこの点こそが Elm の強さの要になるでしょう。結果として得られるのは、次のような長所です。
- パッケージは Elm のやり方に合うように、よりよく設計されるようになります。 Elmコミュニティは経験を積み、自信をもって Elm を使うことができるようになってきました。そのため、とくにレイアウトとデータ可視化の分野では、Elm のエコシステムを活用しやすく、The Elm Architecture とも親和性の高い新しいアプローチを採用したパッケージが見られるようになってきました。他の分野でも、このような革新が起こり続けることを期待しています!
- コードの移植性が保たれます。 将来コンパイラーが x86 や WebAssembly を出力するようになったとしても、エコシステムはそのまま動作し続けますし、そのうえ実行速度は速くなります! ポートの仕組みはすべてのパッケージが Elm だけで書かれていることを保証します。そして Elm 自身も、JavaScript 以外のコンパイラーターゲットを実現できるように設計されているのです。
- パッケージの安全性が向上します。 JavaScriptのような言語にはパッケージ管理そのものに重大なセキュリティ上の懸念があります。資格情報が窃取された事例やAPIキーが窃取された事例は決してめずらしくありません。
window
オブジェクトにキーロガーが仕掛けられる、などの危険があるため、JavaScriptのエコシステムはすべてのパッケージを認証するコストを強いられています。Elmのパッケージシステムは、パッケージの認証にかかるコストと、セキュリティ上のリスクを全体的に抑えつつ、こうした類の漏洩が発生しえないことを保証できるのです。 - 最適化がより容易になります。 コンパイラーが生成するJavaScriptコードは、リリースを追うごとに大きく変化しています。例えばバージョン0.19では、(1)JavaScriptのコードサイズを低減するツールの利用を念頭に置いたコード生成、(2)ユーザーが選択した最適化レベルによってカスタム型の実行時表現を変更する、という2つの最適化によって、アセットサイズを劇的に小さくすることができました。今後も、JavaScriptコードの分割生成や、カリー化された関数のさらに高速な呼び出し規約など、新しい最適化手法が見つかれば、再びコード生成に手を加えることがあるでしょう。さらに、Elmコンパイラーは生成されるコードが純粋であると仮定することによって、他のコンパイラーより積極的にコードを移動させることができます。現時点では、コンパイラーが特定の関数呼び出し規約を強制されると、こういった最適化を不可能にしてしまうおそれがあるのです。
これが長く険しい道のりになることは間違いありませんが、プログラミング言語は30年以上も使われ続けるものです。数十年にわたってチームや企業をサポートする必要があるので、20年から30年先に Elm がどうなっているのかを見据えて考えたとき、このポートによるトレードオフはとても期待できるのではないかと思っています! 私の講演、What is Success?はちょっとスロースタートですが、この事についてより深く踏み込んでいます。
繰り返しになりますが、このやり方はすべての人のためのものではありません。Elmの他にも伝統的なFFIを採用している数多くの言語があるので、あなたにとってより良いと思えるものを試してみるべきです。それらの言語では、パッケージやエコシステムがまとまっていないかもしれません。実行時例外に出くわすことが頻繁にあるかもしれません。それでもなお、柔軟性が重要になる場合があります。Elmの備えるフラグ、ポート、そしてカスタムエレメンツというやり方だけで、あなたのやりたいことが実現できるかどうか、JavaScriptと実際に相互運用しているこれらの例で確かめてみてください。もしあなたがElmを仕事で使うことを考えているなら、この判断は特に重要です!