イントロダクション

Motoko は、Internet Computer 上で動作するスマートコントラクトの Canister の開発に特化した、最新の汎用プログラミング言語です。 この言語は Internet Computer を直接のターゲットにしていますが、将来的に他のターゲットへのコンパイルをサポートするのに十分な程度には一般的な設計になっています。

アプローチ性(言語の親しみやすさ)

Motoko は、JavaScript、Rust、Swift、TypeScript、C#、Java などのモダンプログラミング言語を通してオブジェクト指向や関数型プログラミングのイディオムに基本的な知識を持っているプログラマーが親しみやすいように設計されたモダン言語です。

非同期メッセージングと型安全な実行

Motoko は、分散型アプリケーション(Dapps)のための特別なプログラミング抽象化を含む、モダンプログラミングのイディオムを使用可能にしています。 それぞれの Dapps は、非同期のメッセージパッシング のみで通信する、1つまたは複数の Actor で構成されます。Actor のステートは、他のすべての Actor から分離されており、分散性をサポートしています。複数の Actor 間でステートを共有する方法はありません。 Motoko の Actor ベースのプログラミングの抽象化は、人間が読めるメッセージパッシングのパターンによってプログラミングすることを可能にし、各ネットワークの相互作用が特定のルールに従うことや、よくある間違いを避けることを強制します。

具体的には、Motoko プログラムは実行前に各プログラムをチェックする実用的でモダンな型システムが含まれているため、型健全 です。 Motoko の型システムは、Motoko のプログラムが、可能なすべての入力に対して動的な型エラーを起こさずに安全に実行されるかどうかを静的にチェックします。 その結果、他の言語、特に Web プログラミング言語でよく見られるプログラミング上の落とし穴の類の全てが除外されます。これには、NULL 参照エラー、引数や返り値の型のミスマッチ、フィールドの欠落エラーなどが含まれます。

実行時には、Motoko は、WebAssembly という、モダンコンピュータハードウェアをきれいに抽象化したポータブルなバイナリフォーマットに静的にコンパイルし、インターネット上で広く実行したり、Internet Computer 上で実行したりすることを可能にしています。

Actor としての各 Canister スマートコントラクト

Motoko は、Internet Computer 上の Canister スマートコントラクトのものを含む Service を表現するための Actor ベース のプログラミングモデルを開発者に提供しています。

Actor はオブジェクトに似ていますが、そのステートが完全に分離されており、世界中とのやりとりがすべて 非同期 メッセージングで行われる点が特別です。

Actor との間のすべてのコミュニケーションは、Internet Computer のメッセージングプロトコルを使い、ネットワーク上で非同期にメッセージを渡しています。 Actor のメッセージは順番に処理されるので、ステートの変更が競合状態を認めることはありません(await 式の区切りによって明示的に許可されている場合を除く)。

Internet Computer は、送信された各メッセージが確実に応答を受け取ることを保証します。レスポンスは、ある値を持つ成功ステータスか、エラーのいずれかです。エラーには、受信側の Canister による明示的なメッセージの拒否、ゼロ除算などの不正な命令によるトラップ、配布やリソースの制約によるシステムエラーなどがあります。例えば、システムエラーとは、受信者が一時的または恒久的に利用できないことです(受信 Actor にアクセス集中しているか、削除されているかのいずれか)。

非同期 Actor

他の モダン プログラミング言語と同様に、Motoko はコンポーネント間の 非同期 コミュニケーションのための人間工学的なシンタックスを認めています。

Motoko の場合、通信している各コンポーネントが Actor です。

Actor を 使う 例として(おそらく自分自身も Actor だと考えるのがよいでしょう)、この3行のプログラムを考えてみましょう。

let result1 = service1.computeAnswer(params);
let result2 = service2.computeAnswer(params);
finalStep(await result1, await result2)

このプログラムの動作は、3つのステップでまとめることができます:

  1. プログラムは、Motoko Actor または他の言語で実装された Canister スマートコントラクトとして実装された2つの異なる Service に対して、2つのリクエスト(1行目と2行目)を行います。

  2. プログラムは、各返り値に対してキーワード await を用いて、各返り値の準備ができるのを待ちます(3行目)。

  3. プログラムは、最終ステップ(3行目)で finalStep 関数を呼び出して、両方の結果を使用します。

一般的に言えば、Service は互いに待つのではなく、実行を インターリーブ することで全体の待ち時間を短縮することができます。 しかし、特別な言語サポート なし にこの方法で待ち時間を短縮しようとすると、そのようなインターリーブにはすぐに明快さや単純さが犠牲となります。

インターリーブ実行が ない 場合(例えば、上記の呼び出しが2つではなく1つだけの場合)でも、同じ理由で、プログラミングの抽象化によって明確さと単純さを実現しています。 つまり、プログラムを変換する場所をコンパイラに知らせることで、背後にあるシステムのメッセージパッシングループによる実行をインターリーブするために、プログラマがプログラムロジックを歪めることをせずにすみます。

このプログラムでは、3行目で await を使用することで、そのインターリーブ動作を Motoko が提供する人間が読めるシンタックスでシンプルに表現しています。

このような抽象化がなされていない言語環境では、開発者は単にこれらの2つの関数を直接呼び出すのではなく、非常に高度なプログラミングパターンを採用することになります。おそらくシステムが提供する イベントハンドラ の中に開発者が提供する コールバック関数 を登録することになるでしょう。

各コールバックは、呼び出した関数の返り値の準備ができたときに発生する非同期イベントを処理することになります。このようなシステムレベルのプログラミングは強力ですが、高レベルのデータフローを、共有されているステートを通じて通信する低レベルのシステムイベントに分解することになるため、非常にエラーが発生しやすいです。 このようなプログラミングスタイルが必要な場合もありますが、ここではそうではありません。

私たちのプログラムは、このような煩雑なプログラミングスタイルを避け、代わりにより自然な ダイレクト スタイルを採用しており、各リクエストは通常の関数呼び出しに近い形となっています。 この、よりシンプルで様式化されたプログラミング形式は、今日のほとんどのモダンソフトウェアがそうであるように、外部環境 と相互作用する実用的なシステムの表現としてますます人気が高まっています。

しかし、これには特別なコンパイラと型システムのサポートが必要で、その詳細については後述します。

非同期 動作のサポート

非同期 コンピューティングでは、プログラムとその実行環境は、互いに 並行 して実行される 内部計算 を行うことができます。

具体的には、非同期プログラムとは、プログラムの実行を計算環境に要求した場合に、その完了を待つ必要が(必ずしも)ないものです。 同時に、計算環境が要求された計算を完了するまでの間、プログラムはその環境の中で内部計算を行うことが許されます。上の例では、プログラムは最初の要求が完了するのを待つ前に2番目の要求を発行します。

対称的に、環境側がプログラムに要求することは、プログラムの回答を待つことを(必ずしも)必要としません。 環境側は、プログラム側で答えが生成される間に外部で計算を進行することができます。

上では、この “通知” パターンの例を示していませんが、これはコールバック(および 高次 関数と制御フロー)を使用するため、より複雑になるからです。

async / await 構文

わかりやすさとシンプルさへのニーズに対応するために、Motoko では急速に普及している asyncawait というプログラム構成を採用しています。これは、複雑になりがちな非同期の依存関係グラフを記述するための 構造化された 言語をプログラマにもたらします。

async の構文によって、Future が導入されます。 Future の値は 将来的に非同期に配信される 結果の promise を表します(上の最初の例では示されていません)。 Future については、Actor と async データで Actor を導入した際により詳しく学びます。

ここでは単純に、service1.computeAnswer(params)service2.computeAnswer(params) を呼び出した際に返ってくる値を使用します。

await 構文を用いると Future に同期し、その生成元によって Future が完了するまで計算を中断します。 上の例では、2 つの Service の呼び出しから結果を得るために、await が2つ使われています。

開発者がこれらのキーワードを使用すると、コンパイラは必要に応じてプログラムを変換します。多くの場合、純粋に同期的な言語では手作業で実行するのが面倒な、プログラムの制御フローやデータフローの複雑な変換を行います。 一方、Motoko の型システムでは、型の生成側と使用側を流れる型は常に一致しており、Sercive 間で送信されるデータ型は行き来することが許可されていること、そして(例えば)プライベートな可変型ステート を含まないことなど、これらの構成要素の一定の正しい使用パターンが強制されます。

型と Static

他のモダンプログラミング言語と同様、Motoko では、各変数に関数やオブジェクト、プリミティブなデータ(文字列、単語、整数など)の値を入れることができます。

レコード、タプル、バリアント と呼ばれる “タグ付けされたデータ” など、他の 値の型 も使用可能です。

Motoko は、型の健全性 としても知られる型安全性の形式的な特性を享受しています。 この考え方は、正しく型付けされた Motoko プログラムは間違いを起こさない というフレーズでしばしばまとめられます。これは、データに対して実行される操作は、その静的な型によって許可されるものだけであるという意味です。

例えば、Motoko プログラムの各変数には関連する があり、この型はプログラムが実行される前に 静的に 知られています。 各変数の使用はコンパイラによってチェックされ、NULL 参照エラー、無効なフィールドアクセスなどの実行時の型エラーを防ぎます。

この意味で、Motoko の型は、プログラムのソースコードの中で、信頼できる、コンパイラが検証した ドキュメント を提供します。

通常通り、動的テストでは Motoko の型システムの手の届かないところにあるプロパティをチェックすることができます。 Motoko の型システムはモダンではありますが、意図的に 先進的 ではなく、特に風変わりなものでもありません。 むしろ、Motoko の型システムは、モダンでありながら非常に理解しやすい、実用的な型システムの標準的な概念を統合し、汎用の分散アプリケーションをプログラミングするための、親しみやすく表現力豊かでありながらも安全な言語を提供しています。