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 } |