It’s a tricky question.
There is no tuple type in Candid. Tuple is just a shorthand for record, whose indices happen to be from 0 to n-1. For example, { 0 = "motoko"; 1 = true }
in Candid can be abbreviated as ("motoko", true)
. In Motoko, this means both {_0_ = "motoko"; _1_ = true}
and ("motoko", true)
translate to the tuple shorthand in Candid.
At the top-level in Candid (also the function arguments and return types in Motoko), there is a tuple-like thing, but it’s not really a tuple. For example, a Motoko function that returns async (Text, Bool)
doesn’t return a tuple of type (Text, Bool)
. Instead, it means the function returns TWO values. They are Text
and Bool
respectively.
Now, in your tuple
function, to_candid
encodes a single value (tuple in Motoko, which is a record type in Candid). from_candid
is trying to decode this blob as three values, which causes the runtime error of “not enough values to decode”.
There are two ways to fix this function, depending on whether you want to fix from_candid or to_candid:
public query func tuple() : async ?(Text, Nat, Bool) {
let motoko = ("candid", 1, true);
let blob = to_candid(motoko);
// Cannot use ?(Text, Nat, Bool) type, because that would mean to decode three values.
let ?f : ?{_0_:Text; _1_: Nat; _2_: Bool} = from_candid(blob);
?(f._0_, f._1_, f._2_)
};
public query func tuple2() : async ?(Text, Nat, Bool) {
let blob = to_candid("candid", 1, true);
from_candid(blob)
};
Another minor thing is that there is no unary tuple in Motoko. So in your single_tuple
code, both ("candid")
and ("candid",)
means "candid"
in Motoko. If you want to return a unary tuple in Candid, use {_0_="candid"} in Motoko
.
If you want to be more formal, you can read this spec: motoko/IDL-Motoko.md at master · dfinity/motoko · GitHub