Internet Computer のプログラミング・モデルは, Candid の値をコード化したバイナリ・データの非同期メッセージ交換によって通信する, メモリ隔離されたキャニスタ・スマート・コントラクトから成っています. 一つのキャニスタ・スマート・コントラクトは, 一度に1つのメッセージを処理し, 競合状態を回避します. キャニスタ・スマート・コントラクトは, 自分が発したキャニスタ・スマート・コントラクト間メッセージの戻り値を元に次に何をすべきかをコールバックとして登録します.
The programming model of the Internet Computer consists of memory-isolated canister smart contracts communicating by asynchronous message passing of binary data encoding Candid values. A canister smart contract processes its messages one-at-a-time, preventing race conditions. A canister smart contract uses call-backs to register what needs to be done with the result of any inter-canister smart contract messages it issues.
Motoko は Internet Computer の持つ複雑さを アクタ・モデル という, よく知られた高度な抽象を用いて抽象化しています. 各キャニスタ・スマート・コントラクトは型付けされたアクタとして表現されます. アクタの型は, 自分が扱えるメッセージの一覧です. 各メッセージは, 型付けられた非同期関数として抽象化されています. アクタ型から Condid の型への翻訳によって, 下位層の Internet Computer の生のバイナリ・データ上の構造が決められます. アクタはオブジェクトに似ていますが, 異なるのは, 状態が完全に隔離されていること, 世界との相互作用が完全に非同期メッセージ交換によること, たとえ並行に動作するアクタが並列にメッセージを送ってきたとしても, メッセージは一度に1つずつ処理されることです.
Motoko abstracts the complexity of the Internet Computer with a well known, higher-level abstraction: the actor model. Each canister smart contract is represented as a typed actor. The type of an actor lists the messages it can handle. Each message is abstracted as a typed, asynchronous function. A translation from actor types to Candid types imposes structure on the raw binary data of the underlying Internet Computer. An actor is similar to an object, but is different in that its state is completely isolated, its interactions with the world are entirely through asynchronous messaging, and its messages are processed one-at-a-time, even when issued in parallel by concurrent actors.
Motoko では, アクタへのメッセージ送信は関数呼び出しですが,
In Motoko, sending a message to an actor is a function call, but instead of blocking the caller until the call has returned, the message is enqueued on the callee, and a future representing that pending request immediately returned to the caller. The future is a placeholder for the eventual result of the request, that the caller can later query. Between issuing the request, and deciding to wait for the result, the caller is free to do other work, including issuing more requests to the same or other actors. Once the callee has processed the request, the future is completed and its result made available to the caller. If the caller is waiting on the future, its execution can resume with the result, otherwise the result is simply stored in the future for later use.
Motoko では, アクタには専用の構文と型があります. それは,
f
は, ある型 T
に対して async T
という特殊な型の値になりますf
が完了するのを待っていることは型 T
の値を得るための await f
を使って表現されます.(例えば) オブジェクトや可変配列を送るなどメッセージによって共有状態が紛れ込むのを避けるために, 共有関数を通じて送信されるデータは不変な 共有 型に制限されます.
In Motoko, actors have dedicated syntax and types; messaging is handled by so called shared functions returning futures (shared because they are available to remote actors); a future, f
, is a value of the special type async T
for some type T
; waiting on f
to be completed is expressed using await f
to obtain a value of type T
. To avoid introducing shared state through messaging, for example, by sending an object or mutable array, the data that can be transmitted through shared functions is restricted to immutable, shared types.
まず始めに, いちばん簡単な状態を持つサービス - 以前にローカルで動かした counter
オブジェクトの分散ヴァージョンである Counter
アクタから始めましょう.
To start, we consider the simplest stateful service: a Counter
actor, the distributed version of our previous, local counter
object.
次のアクタ宣言をご覧下さい:
Consider the following actor declaration:
actor Counter {
var count = 0;
public shared func inc() : async () { count += 1 };
public shared func read() : async Nat { count };
public shared func bump() : async Nat {
count += 1;
count;
};
};
Counter
アクタはひとつのフィールドと3つのパブリックな 共有 関数を宣言しています:
The Counter
actor declares one field and three public, shared functions:
フィールド count
は可変で, ゼロに初期化され, 暗黙に private
です.
関数 inc()
は非同期にカウンタを増やし, 同期のために async ()
型のフューチャを返します.
関数 read()
は非同期にカウンタの値を読んで, その値を持つ async Nat
型のフューチャを返します.
関数 bump()
は非同期にカウンタを増やし, カウンタを読み出します.
the field count
is mutable, initialized to zero and implicitly private
.
function inc()
asynchronously increments the counter and returns a future of type async ()
for synchronization.
function read()
asynchronously reads the counter value and returns a future of type async Nat
containing its value.
function bump()
asynchronously increments and reads the counter.
共有関数はローカル関数とは異なり, 遠隔の呼び出し元にアクセスできますが, 引数と返値は 共有 型でなければならないという独自の制約もあります. 共有型には, 不変データ, アクタ参照, 共有関数参照などがありますが, ローカル関数への参照や可変データは含みません. アクタによる通信はすべて非同期なので, アクタの関数はフューチャ, つまりある型 T
に対して async T
形式の型を返す必要があります.
Shared functions, unlike local functions, are accessible to remote callers and have additional restrictions: their arguments and return value must be shared types - a subset of types that includes immutable data, actor references, and shared function references, but excludes references to local functions and mutable data. Because all interaction with actors is asynchronous, an actor’s functions must return futures, that is, types of the form async T
, for some type T
.
Counter
アクタの状態 (count
) を読んだり, 変更したりするには, その共有関数を通じて行うしかありません.
The only way to read or modify the state (count
) of the Counter
actor is through its shared functions.
型 async T
の値がフューチャです. フューチャの生成側は結果 (値かエラーか) を返すときにフューチャを完了します.
A value of type async T
is a future. The producer of the future completes the future when it returns a result, either a value or error.
オブジェクトやモジュールとは異なり, アクタが公開できるのは関数だけで, しかもこの関数は 共有
できるものでなければなりません. この理由から, Motoko はパブリックなアクタ関数に shared
修飾子を付けなくてもより簡潔に同等なアクタ宣言ができるようにしています:
Unlike objects and modules, actors can only expose functions, and these functions must be shared
. For this reason, Motoko allows you to omit the shared
modifier on public actor functions, allowing the more concise, but equivalent, actor declaration:
actor Counter {
var count = 0;
public func inc() : async () { count += 1 };
public func read() : async Nat { count };
public func bump() : async Nat {
count += 1;
count;
};
};
今のところ, 共有関数を宣言できるのは, アクタかアクタ・クラスの本体の中だけです. この制約にも拘わらず, 共有関数は Motoko の中では第一級の値で, 引数や返値として渡したり, データ構造の中に格納したりすることができます.
For now, the only place shared functions can be declared is in the body of an actor or actor class. Despite this restriction, shared functions are still first-class values in Motoko and can be passed as arguments or results, and stored in data structures.
共有関数の型は, 共有関数型を用いて指定されます. 例えば, 値 inc
の型は shared () → async Nat
になり, これを他のサービスへの単独のコールバックとして用いることができます (例は公開-購読 を見てね).
The type of a shared function is specified using a shared function type. For example, the value inc
has type shared () → async Nat
and could be supplied as a standalone callback to some other service (see publish-subscribe for an example).
オブジェクトがオブジェクト型を持つのと同様に, アクタは アクタ型 を持ちます. Counter
アクタは次のような型を持っています:
Just as objects have object types, actors have actor types. The Counter
actor has the following type:
actor {
inc : shared () -> async ();
read : shared () -> async Nat;
bump : shared () -> async Nat;
}