Open6

Motoko Bootcamp

ホーさんホーさん

Candid UI を使った関数のテスト

dfx canister call ... を使ってコマンドラインで関数の引数を与える時は Candid という言語で指定する必要があり、慣れていないと難しいです。
ブラウザで関数を実行できる Candid UI という仕組みがあるのでこちらを使うことをお勧めします。
使い方は ドキュメントを参照してください

Candid UI で関数をテストした後のログを見ると、

このようにCandid での書き方を教えてくれるので、

dfx canister call day1 selection_sort '(vec {5;2;1;6;3})'

とするとコンソールでテストもできます。

ホーさんホーさん

末尾のセミコロンをお忘れなく

以下のようなエラーが出るときはセミコロンをつけ忘れています。

.9-47.12: syntax error [M0001], unexpected token 'add', expected one of token or <phrase> sequence:
  }
  ; seplist(<dec>,<semicolon>)

特にハマりがちなのが、複数の関数(func)が定義されていた時に、末尾に定義されている関数はセミコロンがなくても動く ということです。(動いていた Actor に新しく関数を追加しただけのはずなのに動かない!変えた場所は間違ってないように見える・・というような場面)
Actor 内に関数を追加していく場合、既存の末尾の関数にセミコロンがついているか確認してください。

actor {
    public func greet(name : Text) : async Text {
        return "Hello, " # name # "!";
    }
   /// ↑セミコロンがなくても動く
};
actor {
    public func greet(name : Text) : async Text {
        return "Hello, " # name # "!";
    }
   /// ↑セミコロンがないと動かない!!

    public func greet2(name : Text) : async Text {
        return "Hello again, " # name # "!";
    }
   /// ↑セミコロンがなくても動く
};
actor {
    public func greet(name : Text) : async Text {
        return "Hello, " # name # "!";
    };
   /// ↑セミコロンをつけると動く

    public func greet2(name : Text) : async Text {
        return "Hello again, " # name # "!";
    }
   /// ↑セミコロンがなくても動く
};
actor {
    public func greet(name : Text) : async Text {
        return "Hello, " # name # "!";
    };
   /// ↑セミコロンをつけると動く

    public func greet2(name : Text) : async Text {
        return "Hello again, " # name # "!";
    };
   /// ↑セミコロンがついててもOK(というより毎回つけたほうがミスらない)
};

関数を閉じる時にはセミコロンをつける習慣をつけたほうが良いと思いますが、最後の関数はセミコロンがなくても動いちゃう点にご注意を。

ホーさんホーさん

ミュータブル変数への代入

ミュータブルな変数(var)への代入操作は := を使う点にご注意ください。

var num = 2;
num := 40;

加算と同時に代入する場合などでは見慣れたシンタックスになります。

var num = 2;
num += 40;
ホーさんホーさん

ミュータブルな配列

配列の中身を後から変えたい場合には、ミュータブルな配列 [var Nat] を使います。

let a : [var Nat] = [var 1, 2, 3];
a[2] := 42;

Actor 間ではミュータブルな変数をやり取りすることはできません。
つまり、actor の関数ではミュータブル変数を返すことはできません。

/// async [var Nat] としちゃだめ
public func test(): async [var Nat] {
 ...
}
/// async [Nat] ならOK
public func test(): async [Nat] {
 ...
}

関数内で [var Nat] で定義した変数を関数から返したい場合があるかもしれません。
逆に、イミュータブルな配列 [Nat] をミュータブルな配列の初期値として渡したい場面もあるかもしれません。
そんな時は Motoko 標準ライブラリのArray に定義されているメソッドを使いましょう。

イミュータブルな配列からミュータブルな配列を作成

// b は [Nat]
let a: [var Nat] = Array.thaw<Nat>(b); 

ミュータブルな配列からイミュータブルな配列を作成

// a は [var Nat]
let b: [Nat] = Array.freeze<Nat>(a);

ミュータブル/イミュータブルな変数に関するドキュメントにも目を通すと良いと思います。

<> で囲まれているのはジェネリクスで、使われるまで型が決まらないようないろいろな型の値を受け入れられる関数/クラスを作るときに使うものです。利用時に自由に型を入れることができます。
今回は Array.freeze()関数にわたす配列が Nat 型ですよということを Array.freeze<Nat>()とすることで利用時に教えてあげています。
他のプログラミング言語でもよく使われる機能なので初めての方はこちらなどから確認してください。

ホーさんホーさん

actor {} と module {} の違いって?

外部の Canister や Candid UI から触れるのは、actor {} 内で定義された関数です。
module {} は Canister の内部でのみ使える関数で、actor に含めたくないヘルパー関数などを定義しておきます。

例えば、utils.mo を作ってファイルの中身が以下だとします。

module {
  public func greetInUtil(name: Text): Text {
    return "Hello, " # name # "!";
  }
}

同じフォルダ内にある main.mo からは以下のように呼び出しができます

import Utils "utils";

actor {
  public func greet(name: Text) : async Text {
    return Utils.greetInUtil(name);
  }
}

ポイントとして、module 内で定義された関数は Canister 内で使うだけなので async をつける必要がありません。
また、上でミュータブルな配列は actor の関数から返せないと説明しましたが、module の関数からはミュータブルな変数を返しても問題ないです。
あくまで、Canister 間で共有する関数の返し値がミュータブルじゃだめ、ということです。

関連ドキュメントはこちらです。moduleの使い方はドキュメント見てもわかりにくいかもしれませんが、ここで説明した通りです。

ホーさんホーさん

core_project/example が立ち上がらない

公式のREADMEの手順では立ち上がらないので、以下に手順を記載します。

すでに立ち上がっているローカル開発環境(レプリカ)を止める

新しい Canister に取り掛かるときは、既存のローカル開発環境が止まっていることを確認しましょう。

dfx stop

dfx のバージョンが最新であることを確認する

core_project/example は dfx の最新バージョンである 0.9.2 を使用しているので、必要に応じてアップグレードします。

dfx upgrade

npm でライブラリをインストールする

次にフロントエンドで必要なライブラリをインストールします。

bootcamp/core_project/example

のディレクトリに移動して、以下を実行してください。

npm install

ローカル開発環境(レプリカ)を起動する

ここはいつも通りです。
bootcamp/core_project/example ディレクトリで以下を実行します。

dfx start

Canister をデプロイする

Canister をデプロイします。ここで注意が必要なのは、
minter の canister が actor クラスになっているため、デプロイ時に引数が必要であることです。
minter/main.moを見ますと、

actor class DRC721(_name : Text, _symbol : Text) {
...
}

となっており、_name_symbol の二つの Text 型の引数が必要であることがわかります。
なので、それぞれ "sample_nft""NFT" とここでは指定してみます。

bootcamp/core_project/example ディレクトリで以下を実行します。

dfx deploy --argument '("sample_nft","NFT")'

フロントエンドを起動する

bootcamp/core_project/example ディレクトリで以下を実行します。

npm run start

フロントエンドにブラウザからアクセスする

ブラウザからフロントエンドの Canister にアクセスします。
ここで、Canister の ID が必要ですので、コマンドを使って確認します。

bootcamp/core_project/example ディレクトリで以下を実行します。

dfx canister id minter_assets

ID が表示されると思いますので、<CANISTER_ID> と置き換えてアクセスしてください。

http://localhost:8000/?canisterId=<CANISTER_ID>

うまくいけばブラウザに表示されます。

お疲れ様でした。