Motoko の概要
ここでは、読者の理解の助けになるように、他の言語で見られるオペレーションやパターンが Motoko ではどうなるのかをまとめています。主要な機能の例を挙げて Motoko の概要をシンプルに、かつ包括的に説明します。
Motoko の動機と目標
Motoko は、DFINITY と Internet Computer プラットフォーム のためのシンプルで使いやすい言語です。
-
よく見られるシンタックス
-
デフォルトで安全
-
Canister モデルを使用したスマートコントラクトの組み込み
-
DFINITY と Internet Computer プラットフォーム の機能のシームレスな統合を提供
-
現在、そして将来の WebAssembly を最大限に活用
設計のポイント
Motoko は、Java、JavaScript、C#、Swift、Pony、ML、Haskell など、いくつかのプログラミング言語からインスピレーションを受けています。
-
オブジェクト指向、関数型、命令型
-
メンバーの record としてのオブジェクト
-
async
/await
による非同期メッセージングのシーケンシャルプログラミング -
シンプルなジェネリクスと派生型付けによる構造的な型付け
-
安全な算術 (Unbounded とチェック)
-
デフォルトでは Non-nullable 型
-
JavaScript のようなシンタックスでありながら、静的に型付けされており安全
セマンティクス
-
値渡し(Java、C、JS、MLと同様で、HaskellやNixとは異なる)
-
宣言は局所・相互再帰
-
パラメトリック・有界量化
-
型強制ではなく包摂的な派生型付け
-
動的キャストなし
-
継承なし
実装
-
OCaml で実装(
wasm
ライブラリを利用) -
シンプルなリファレンスインタプリタ
-
WebAssembly へのシンプルなコンパイラ
-
各パスで型付けされた IR によるマルチパス
-
統一された表現、Unboxed な算術
-
Two-space GC、メッセージ間 GC
-
消去によるポリモーフィズム
言語の特徴
この章では、Motoko プログラミング言語の機能を簡単に紹介します。 ここで紹介している機能やその他の機能の使い方については、言語のクイックリファレンス の Motoko プログラミング言語のガイド を参照してください。
プリミティブ型
次の章では、Motoko プログラミング言語のプリミティブ型について説明します。
制限あり数(トラップあり)
Nat8
, Nat16
, Nat32
, Nat64
, Int8
, Int16
, Int32
, Int64
-
オーバーフロー、アンダーフロー時にトラップ
-
型アノテーションの指定が必要
-
リテラル:
13
,0xf4
,-20
,1_000_000
浮動小数点数
Float
-
IEEE 754 の倍精度(64ビット)セマンティクス、正規化された NaN
-
小数点以下のリテラルの推論
-
リテラル:
0
、-10
、2.71
、-0.3e+15
、3.141_592_653_589_793_12
関数
次の章では、プログラミング言語 Motoko で関数を扱うための例を紹介します。
関数型
シンプルな関数
Int.toText : Int -> Text
複数の引数と返り値
divRem : (Int, Int) -> (Int, Int)
は、ジェネリクス、ポリモーフィックでは以下のようになります:
Option.unwrapOr : <T>(?T, default : T) -> T
第一級関数(引き渡し、保管が可能)
map : <A, B>(f : A -> B, xs : [A]) -> [B]
let funcs : [<T>(T) -> T] = …
関数の宣言と使用
func() { … }
は func() : () = { … }
の短縮形
パラメトリック関数
型のインスタンス化は省略されることがあります。
匿名関数(ラムダ関数)
func add(x : Int, y : Int) : Int = x + y;
func applyNTimes<T>(n : Nat, x : T, f : T -> ()) {
if (n == 0) return;
f(x);
applyNTimes(n-1, x, f);
}
applyNTimes<Text>(10, "Hello!", func(x) = { Debug.print(x) } );
複合型
次の章では、プログラミング言語 Motoko で複合型を扱う例を紹介します。
タプル型
(Bool, Float, Text)
イミュータブル、異なる型を格納可能、固定サイズ
let tuple = (true, 1.2, "foo");
tuple.1 > 0.0;
let (_,_,t) = tuple;
オプション型
?Text
は、その型の値か、null
func foo(x : ?Text) : Text {
switch x {
case (null) { "No value" };
case (?y) { "Value: " # y };
};
};
foo(null);
foo(?"Test");
配列型(イミュータブル)
[Text]
let days = ["Monday", "Tuesday", … ];
assert(days.len() == 7);
assert(days[1] == "Tuesday");
// days[7] はトラップ (固定長)
for (d in days.vals()) { Debug.print(d) };
配列型 (ミュータブル)
[var Nat]
let counters = [var 1, 2, 3];
assert(counters.len() == 3);
counters[1] := counters[1] + 1;
// counters[3] はトラップ (固定長)
レコード型
{name : Text; points : var Int}
let player = { name = "Joachim"; var points = 0 };
Debug.print(
player.name # " has " #
Int.toText(player.points) # " points."
);
player.points += 1;
プラットフォームの機能
次の章では、プログラミング言語 Motoko のプラットフォーム固有の機能の例を紹介します。
Actor 型
-
オブジェクト型に似ていますが、
actor
としてマークされています。 -
sharable の引数と、no または async の返り値型。
-
“canister” ≈ “actor”
type Receiver = actor { recv : Text -> async Nat };
type Broadcast = actor {
register : Receiver -> ();
send : Text -> async Nat;
}
Sharable 型 ≈ シリアライズ
-
全てプリミティブ型
-
レコード、タプル、配列、バリアント、オプションなどであり、イミュータブルで Sharable なコンポーネントであるもの
-
actor
型 -
shared
関数型
以下は sharable ではない:
-
ミュータブルなもの
-
ローカル関数
-
オブジェクト (メソッドを持つもの)
完全な Actor の例
典型的な Canister の main ファイル
import Array "mo:base/Array";
actor {
var r : [Receiver] = [];
public func register(a : Receiver) {
r := Array.append(r, [a]);
};
public func send(t : Text) : async Nat {
var sum := 0;
for (a in r.values()) {
sum += await a.recv(t);
};
return sum;
};
}
Async/await
async T
-
非同期の future や promise
-
async { … }
によって導入(async 関数宣言では暗黙的)。 -
await e
はe
の結果を待つために計算をサスペンドする。
Actor のインポート
import Broadcast "ic:ABCDEF23";
actor Self {
public func go() {
Broadcast.register(Self);
};
public func recv(msg : Text) : async Nat {
…
}
}
Principal と Caller
プリンシパル型は、ユーザーや Canister/Actor の ID を表します。
actor Self {
let myself : Principal = Principal.fromActor(Self);
public shared(context) func hello() : async Text {
if (context.caller == myself) {
"Talking to yourself is the first sign of madness";
} else {
"Hello, nice to see you";
};
};
}
型システム
次の章では、プログラミング言語 Motoko で使用されている型システムの詳細を紹介します。
構造
型定義は、型を作るのではなく、既存の型に名前を付けます。
type Health1 = { #invincible; #alive : Nat; #dead };
type Health2 = { #invincible; #alive : Nat; #dead };
let takeDamage : (Health1, Nat) -> Health1 = …;
let h : Health2 = #invincible;
let h' = takeDamage(h, 100); // 動作します
クラス宣言の例
次の表は、Motoko のクラス宣言と、JavaScript や TypeScript のクラス宣言を比較したものです。
Motoko | JavaScript/TypeScript |
---|---|
class Counter(initValue:Nat) { var _value = initValue; public func get() : Nat { _value }; func f(x: Nat) {}; } |
class Counter { private _value; constructor(initValue) { _value = initValue } public get() { return _value } private f(x) {} } |
class Foo() = Self { func f() : Foo = Self } |