原子类型:
- int 是 257 位有符号整数的类型。默认情况下,启用了溢出检查,并且会导致整数溢出异常。
- cell 是 TVM 单元的类型。TON 区块链中的所有持久数据都存储在由单元组成的树结构中。每个单元最多可以包含 1023 位的任意数据,并且最多可以引用其他四个单元。单元在基于堆栈的 TVM 中充当内存。
- slice 是单元切片的类型。一个单元可以被转换为一个切片,随后可以通过从切片中加载数据位和对其他单元的引用来获取这些内容。
- builder 是单元构建器的类型。可以将数据位和对其他单元的引用存储在构建器中,然后将构建器最终化为一个新的单元。
- tuple 是 TVM 元组的类型。元组是最多包含 255 个组件的有序集合,这些组件可以是任意类型,且可能各不相同。
- cont 是 TVM 延续的类型。延续用于控制 TVM 程序的执行流程。尽管从 FunC 的角度来看,延续是一个相当底层的对象,但它实际上非常通用。
请注意,上述任何类型在 TVM 堆栈中都只占用一个条目。
布尔类型的缺失
在 FunC 中,布尔值由整数表示;
false
表示为 0,true
表示为 -1(即二进制表示中的 257 个 1)。逻辑操作是通过按位操作来完成的。当检查条件时,任何非零整数都被视为 true
值。空值Null
在 TVM 中,
Null
类型的值用于表示缺失的原子类型值。在 FunC 语言中,一些标准库的函数或操作符会声明它们返回某个原子类型,但在某些情况下,它们实际上可能返回 null
。同样地,某些函数声明它们接受一个原子类型的参数,但实际上也可以接受 null
作为输入而不报错。这种行为在这些函数的具体说明中会被明确指出。默认情况下,null
值是非法的,并会在运行时引发异常。因此,原子类型
A
可以被隐式转换为 A^?
,也叫做 Maybe A
。类型检查器不会阻止这种转换。Hole 类型
FunC 支持类型推断。类型
_
和 var
被称为类型“hole”,这些类型在类型检查时可以被推断为具体的类型。例如,
var x = 2;
定义了一个变量 x
,并将其赋值为 2
。类型检查器可以推断出 x
的类型为 int
,因为 2
是 int
类型,而赋值操作要求左右两边的类型相同。复合类型
类型可以组合形成更复杂的类型。
函数类型
形如
A -> B
的类型表示一个函数,其定义了函数的参数类型和返回类型。例如,int -> cell
表示一个函数,该函数接收一个整数参数并返回一个 TVM 的 cell
。在内部,这类类型的值被表示为延续(continuations)。
张量类型
形如
(A, B, ...)
的类型本质上表示类型为 A, B, ...
的值的有序集合,这些值共同占据多个 TVM 的栈条目。例如,若某个函数
foo
的类型为 int -> (int, int)
,它意味着该函数接收一个整数并返回一对整数。调用这个函数的代码可能是
(int a, int b) = foo(42);
。在内部,函数消费一个栈条目并留下两个栈条目。从底层的角度看,类型为
(int, (int, int))
的值 (2, (3, 9))
和类型为 (int, int, int)
的值 (2, 3, 9)
被相同地表示为栈上的三个条目 2, 3, 9
,但对于 FunC 的类型检查器来说,它们是不同类型的值。例如,代码 (int a, int b, int c) = (2, (3, 9));
将无法通过编译。张量类型的一个特殊情况是单位类型(unit type)
()
,它通常表示一个函数没有返回值或没有参数。例如,函数 print_int
的类型是 int -> ()
,而函数 random
的类型是 () -> int
。单位类型有一个唯一的值 ()
,该值占用 0 个栈条目。类型
(A)
被类型检查器视为与 A
相同的类型。元组类型
形如
[A, B, ...]
的类型表示TVM 元组,这些元组具有已知的长度和在编译时已确定的组件类型。例如,[int, cell]
是一种 TVM 元组类型,它的长度为 2,第一个元素是整数,第二个元素是 cell
。[]
表示空元组的类型(唯一的值是空元组)。与单位类型 ()
不同,空元组 []
占用一个栈条目。多态性
在FunC语言中,多态性(polymorphism)通过类型变量实现,使得函数可以处理任意数据类型,提高了代码的灵活性。例如:
forall X -> (X, X) duplicate(X value) { return (value, value); }
这个例子中的
duplicate
函数是多态的,因为它能够接受任何类型 X
的参数。函数接收一个值 value
并返回该值的两个副本。如果调用 duplicate(6)
,会返回两个 6
;如果调用 duplicate([])
,则返回两个空元组 [] []
。这里的
X
就是一个类型变量,表示函数能够适用于任何具体类型。用户定义的类型
目前,FunC 不支持用户自定义类型,只能使用已有的类型构造。
类型宽度
每种类型的值占用固定数量的栈条目。如果所有值的栈条目数量是相同且已知的,那么我们称该类型具有固定的宽度。目前,多态函数仅支持那些具有固定和已知宽度的类型。
在 FunC 语言中,注释的使用方式包括单行注释和多行注释。
单行注释:
用两个分号
;;
表示,例如:int x = 1; ;; 将 1 赋值给
多行注释:
多行注释用
{-
开始,-}
结束。FunC 支持嵌套多行注释,这与许多其他语言有所不同。例如:{- 这是一个多行注释 {- 这是嵌套在注释中的注释 -} -}
注释的嵌套与优先级:
FunC 允许在多行注释中嵌入单行注释,单行注释
;;
的优先级高于多行注释 {- -}
。在以下例子中,单行注释使得多行注释提前结束,代码 const a = 10;
被注释掉:{- 注释开始 ;; 这个注释标志的结束是注释本身 -> -} const a = 10; ;; 这个注释的开始是注释标志 -> {- 注释结束 -}
因此,
const a = 10;
被多行注释覆盖并被注释掉,而不会被编译。