Motoko 一览
本摘要提供了对 Motoko 的简单而全面的概述,并提供了一些关键特性示例,可帮助您识别您在其他语言中可能知道的操作和模式,以及它们在 Motoko 中的样子。
Motoko 动机和目标
DFINITY 和 Internet Computer platform 的一种简单、有用的语言。
- 
熟悉的语法
 - 
默认安全
 - 
使用 canister 模型结合智能合约
 - 
提供 DFINITY 和 Internet Computer platform 功能的无缝集成
 - 
充分利用现在和未来的 WebAssembly
 
关键设计点
Motoko 从多种编程语言中汲取灵感,包括 Java、JavaScript、C#、Swift、Pony、ML、Haskell。
- 
面向对象、函数式和命令式
 - 
对象作为成员的记录
 - 
async/await用于异步消息的顺序编程 - 
具有简单泛型和子类型的结构类型 *安全算术(无界和检查)
 - 
默认情况下不可为空的类型
 - 
类似 JavaScript 的语法,但静态类型和理智
 
实现
- 
在 OCaml 中实现(利用
wasm库) - 
简单的参考解释器
 - 
WebAssembly 不太简单的编译器
 - 
多通道,每通道输入 IR
 - 
统一表示,未装箱算术
 - 
两个空格 gc,消息之间的 gc
 - 
擦除多态性
 
语言特点
接下来的部分以简化的形式重点介绍 Motoko 编程语言功能。 有关使用这些和其他功能的更多信息,请参阅Language quick reference in the ink:../language-guide/motoko.html[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编程语言中使用复合类型的例子。
元组
(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] will trap (fixed size)
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] will trap (fixed size)
记录
{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: - 
shareable 参数和 no 或 async 结果类型。
 - 
“canister” ≈ “actor”
 
type Receiver = actor { recv : Text -> async Nat };
type Broadcast = actor {
  register : Receiver -> ();
  send : Text -> async Nat;
}
完整的actor示例
典型的容器主文件
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;
  };
}
类型系统
下一节重点介绍 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); // works
Language comparison cheat sheet
Literals
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
> 3; 3 : Nat  | 
# 3;; - : int = 3  | 
> 3 3  | 
> 3.141; 3.141 : Float  | 
# 3.141;; - : float = 3.141  | 
> 3.141 3.141  | 
> “Hello world”; “Hello world” : Text  | 
# “Hello world”;; - : string = “Hello world”  | 
> “Hello world” “Hello world”  | 
> ‘J’; ‘J’ : Char  | 
# ‘J’;; - : char = ‘J’  | 
Does not have   | 
> true; true : Bool  | 
# true;; - : bool = true  | 
> true true  | 
> ();  | 
# ();; - : unit = ()  | 
> undefined  | 
> (3, true, “hi”); (3, true, “hi”) : (Nat, Bool, Text)  | 
# (3, true, “hi”);; - : int * bool * string = 3, true, “hi”  | 
> [3, true, “hi”] [3, true, “hi”]  | 
> [var 1, 2, 3]; [1, 2, 3] : [var Nat]  | 
# [|1; 2; 3|];; - : int array = [|1; 2; 3|]  | 
> [1, 2, 3] [1, 2, 3]  | 
> [1, 2, 3]; [1, 2, 3] : [Nat]  | 
# [1; 2; 3];; - : int list = [1; 2; 3]  | 
> [1, 2, 3] [1, 2, 3]  | 
Expressions
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
-3*(1+7)/2%3  | 
-3*(1+7)/2 mod 3  | 
-3*(1+7)/2%3  | 
-1.0 / 2.0 + 1.9 * x  | 
-1.0 /. 2.0 +. 1.9 *. x  | 
-1 / 2 + 1.9 * x  | 
a || b && c  | 
a or b and c  | 
a || b && c  | 
Functions
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
func<T1,T2,T3>(f : (T1, T2) -> T3) : T1 -> T2 -> T3 = func(x : T1) : T2 -> T3 = func(y : T2) : T3 = f(x,y)  | 
fun f -> fun x -> fun y -> f (x, y) or fun f x y -> f (x, y)  | 
f => x => y => f(x,y)  | 
func<T1, T2, T3>(f : (T1, T2) -> T3, x: T1, y : T2) : T3 = f (x,y)  | 
fun (f, x, y) -> f (x, y)  | 
([f, x, y]) => f(x,y)  | 
func f<T>(x:T) : T = x  | 
let f x = x  | 
f(x) { x }
 | 
Does not have function pattern matching func(x : Int) : Int =
  switch(x) {
    case (0) 0;
    case (n) 1;
  };
 | 
function 0 -> 0
         | n -> 1
 | 
Control flow
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
if (3 > 2) “X” else “Y”  | 
if 3 > 2 then “X” else “Y”  | 
if (3 > 2) { “X” } else { “Y” }
 | 
import Debug “mo:base/Debug”; if (3 > 2) Debug.print(“hello”);  | 
if 3 > 2 then print_string “hello”  | 
if (3 > 2) console.log(“hello”)  | 
while (true) {
  Debug.print(“X”);
}
 | 
while true do print_string “X” done  | 
while(true) {
  console.log(“X”);
}
 | 
label L loop {
  if (x == 0) break L
  else continue L;
} while (true);
 | 
没有 do while 循环——使用递归或 while  | 
do {
  if (x === 0) break;
  else continue;
} while (true);
 | 
import Iter “mo:base/Iter”;
for (i in Iter.range(1,10)) {
  Debug.print(“X”);
};
 | 
for i = 1 to 10 do print_string “X” done  | 
for (i = 1; i <= 10; i++) {
  console.log(“X”);
}
 | 
print_string “hello”; print_string “world”  | 
print_string “hello”; print_string “world”  | 
console.log(“hello”); console.log(“world”);  | 
Value declarations
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
let name = expr;  | 
let name = expr  | 
const name = expr  | 
let f = func<T1, T2>(x : T1) : T2 { expr };
 | 
let f x = expr  | 
const f = x => expr  | 
let fib = func(n : Nat) : Nat {expr};
 | 
let rec fib n = expr  | 
const fib = n => expr  | 
Type declarations
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
type T = Int32 -> Bool  | 
type t = int -> bool <int is 31-bit signed int>  | 
type t = (x: number) => boolean;  | 
type AssocList<K,V> = List<(K,V)>  | 
type (‘a, ‘b) assoc_list = (‘a * ‘b) list  | 
not applicable  | 
type option<T> = ?T  | 
type ‘a option = None  | 
Some of ‘a  | 
type option<T> = T?  | 
type T = {#a : Int32; #b : U}; type U = (T, T);  | 
type t = A of int  | 
B of u and u = t * t  | 
not applicable  | 
type Complex = {#c : (Float, Float)}; func complex(x : Float, y : Float) : Complex = #c(x,y); func coord(#c(x, y) : Complex) : (Float, Float) = (x, y);  | 
Pattern matching
| Motoko | Ocaml | 
|---|---|
func get_opt<T>(opt : ?T, d : T) : T {
  switch(opt) {
    case (null) d;
    case (?x) x;
  };
}
 | 
let get_opt (opt, d) =
  match opt with
    None -> d
  | Some x -> x
 | 
Does not have guards — use if import prelude “mo:base/Prelude”;
func fac(x : Nat) : Nat {
  switch(x) {
    case (0) 1;
    case (n) if (n>0) n * fac(n-1) else Prelude.unreachable();
  };
}
 | 
let rec fac = function 0 -> 1 | n when n>0 -> n * fac(n-1) | _ -> raise Hell  | 
Does not have as a pattern  | 
let foo ((x,y) as p) = (x,p,y)  | 
Tuples
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
type Foo = (Int32, Float, Text)  | 
type foo = int * float * string  | 
type foo = (number, number, string)  | 
let bar = (0, 3.14, “hi”)  | 
let bar = (0, 3.14, “hi”)  | 
const bar = [0, 3.14, “hi”]  | 
let x = bar.1 or let (_, x, _) = bar  | 
let _, x, _ = bar in x  | 
const x = bar[1]  | 
Records
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
type foo = {x : Int32; y : Float; var s : Text}
 | 
type foo = {x:int; y:float; mutable s:string}
 | 
Everything is mutable type foo = {
  x: number; y: number;
  s: string
}
 | 
let bar = {x=0; y=3.14; var s=””}
 | 
let bar = {x=0; y=3.14; s=””}
 | 
const bar = {x:0; y:3.14; s:””}
 | 
bar.x bar.y bar.s  | 
bar.x bar.y bar.s  | 
bar.x bar.y bar.s  | 
Does not do pattern matching on mutable fields let {x=x; y=y} = bar
let {y=y} = bar
or
let {x;y} = bar
let {y} = bar
 | 
let {x=x; y=y; s=s} = bar
let {y=y} = bar
or
let {x;y;s} = bar
let {y;_} = bar
 | 
|
bar.s := “something”  | 
bar.s <- “something”  | 
bar.s = “something”  | 
type Bar = { f: <T>T -> Int32 }
 | 
type bar = { f:’a.’a->int }
 | 
type bar = {
  f<T>(x:T): number;
}
 | 
References and mutable variables
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
var r = 0;  | 
let r = ref 0  | 
let r = new Number(0) // object reference or let r = 0 // mutable variable  | 
r  | 
!r or r.contents  | 
r  | 
r := 1  | 
r := 1 or r.contents <- 1  | 
r = 1  | 
Does not take mutable variables func f(x : Nat) : Nat = x  | 
let f {contents=x} = x
 | 
|
r1 == r2 r1 != r2  | 
r1 == r2 r1 != r2  | 
r1.valueOf() === r2.valueOf() r1 !== r2  | 
Comparison
| Motoko | Ocaml | JavaScript/TypeScript | 
|---|---|---|
2 == 2 2 != 3  | 
2 = 2 2 <> 3  | 
2 === 2 2 !== 3  | 
Does not have references var r = 2; var s = 2; r == s  | 
let r = ref 2 r == r r != ref 2  | 
|
Does not have generic equality  | 
Does not have a generic equality (2, r) != (2, r) (2, r) = (2, ref 2)  | 
(2, r) === (2, r)  | 
Immutable and mutable arrays
| Motoko | Ocaml | 
|---|---|
import Array “mo:base/Array”; Array.tabulate(20, func(x:Nat):Nat = x*x)  | 
List.init 20 (fun x -> x*x)  | 
Array.init(20, 1.0)  | 
Array.make 20 1.0  | 
a[2]  | 
Array.get a 2 a.(2)  | 
a[2] := x  | 
Array.set a 2 x or a.(2) <- x  | 
for (x in a.vals()) {
  Debug.print(x)
}
 | 
List.iter print_string a  | 
Class declaration example
以下表格用以对比在类宣告在proglang,JavaScript/TypeScript中的区别 The following table compares class declarations in Motoko with class declarations in JavaScript and 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
}
 |