TL类型语言

TL (类型语言) 用于描述所使用的类型、构造函数和现有函数系统。实际上,它采用了二进制数据序列化中提出的组合子描述格式。

参见:

进阶主题:

概述

TL 程序通常由两个部分组成,这两个部分用关键字分隔---functions---。第一部分包含内置类型和聚合类型(即它们的构造函数)的声明。第二部分包含已声明的函数,即函数式组合器。

实际上,第一部分和第二部分都包含组合器声明,每个声明都以分号结尾。但是,第一部分只包含构造函数,而第二部分只包含函数。每个组合器都使用上述格式的“组合器声明”进行声明。但是,组合器编号和字段名称可以显式指定。

如果在声明函数之后还需要进行额外的类型声明,则使用关键字(节分隔符)---types---。此外,如果函数组合器的结果类型以感叹号开头,则可以在类型节中声明该函数组合器(实际上,在解释函数节时,会自动添加此感叹号)。

要明确定义组合器的 32 位名称,在组合器名称后紧跟一个井号 (#),然后是 8 个十六进制数字。

命名空间

<namespace_identifier>.<constructor_identifier>像 ` &&` 这样的复合结构<namespace_identifier>.<Type_identifier>可以用作构造函数标识符或类型标识符。句点左侧的标识符部分称为命名空间。此外,类型标识符首字母大写、构造函数标识符首字母小写的规则也适用于句点之后的构造函数部分。例如,`&&`auth.Message是一个类型,而 `&&`auth.std_message是一个构造函数。

命名空间不需要特殊声明。

评论

注释与 C++ 中的注释相同。

例子

// built-in types
int#a8509bda ? = Int;
long ? = Long;
double ? = Double;
string ? = String;
null = Null;

vector {t:Type} # [ t ] = Vector t;
coupleInt {alpha:Type} int alpha = CoupleInt<alpha>;
coupleStr {gamma:Type} string gamma = CoupleStr gamma;  
/* The name of the type variable is irrelevant: "gamma" could be replaced with "alpha"; 
   However, the combinator number will depend on the specific choice. */

intHash {alpha:Type} vector<coupleInt<alpha>> = IntHash<alpha>;
strHash {alpha:Type} (vector (coupleStr alpha)) = StrHash alpha;
intSortedHash {alpha:Type} intHash<alpha> = IntSortedHash<alpha>;
strSortedHash {alpha:Type} (strHash alpha) = StrSortedHash alpha;

// custom types
pair x:Object y:Object = Pair;
triple x:Object y:Object z:Object = Triple;

user#d23c81a3 id:int first_name:string last_name:string = User;
no_user#c67599d1 id:int = User;
group id:int title:string last_name:string = Group;
no_group = Group;

---functions---

// Maybe some built-in arithmetic functions; inverse quotes make "identifiers" out of arbitrary non-alphanumeric strings
`+` Int Int = Int;
`-` Int Int = Int;
`+` Double Double = Double;
// ...

// API functions (aka RPC functions)
getUser#b0f732d5 int = User;
getUsers#2d84d5f5 (Vector int) = Vector User;

在这种情况下,user构造函数被显式地赋值了一个数字(0xd23c81a3);实际上,这是没有必要的,因为该值是字符串的 CRC32 值"user id:int first_name:string last_name:string = User",默认情况下会使用该值。

Vector int、Vector User、Vector Object 等不需要特殊的构造函数——同一个通用构造函数可以在任何地方使用:

vector#1cb5c415 {t:Type} # [ t ] = Vector t;

请注意,在getUsers (Vector int) = Vector User;计算构造函数编号时,会计算字符串“getUsers Vector int = Vector User”的 CRC32 值(其中所有括号均已删除)。

符号表示法T0<T1,T2,...,Tn>是语法糖(T0 (T1) (T2) ... (Tn))。例如,Vector<User>和(Vector User)完全可以互换使用。

RPC 查询示例

假设我们要调用某个查询getUsers([2,3,4])。该查询将被序列化为一系列 32 位整数,如下所示:

0x2d84d5f5 0x1cb5c415 0x3 0x2 0x3 0x4

请注意,TL 序列化会生成 32 位整数序列。当需要将其嵌入字节流(例如网络数据包)时,每个 32 位整数都以小端序用四个字节表示。因此,上述查询对应于以下字节流:

F5 D5 84 2D 15 C4 B5 1C 03 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00

回复可能如下所示:

0x1cb5c415 0x3 0xd23c81a3 0x2 0x74655005 0x00007265 0x72615006 0x72656b 0xc67599d1 0x3 0xd23c81a3 0x4 0x686f4a04 0x6e 0x656f4403

这大致相当于

[{"id":2,"first_name":"Peter", "last_name":"Parker"},{},{"id":4,"first_name":"John","last_name":"Doe"}]

请注意,两种情况下都使用了相同的通用构造函数vector#1cb5c415:请求中用于序列化类型为 `<T>` 的值,响应中Vector int用于序列化类型为 `<T>` 的值。由于在两种情况下,被序列化(反序列化)的值的类型在序列化(反序列化)开始之前就已经确定,因此不存在歧义。例如,服务器在收到查询后,看到第一部分是`<T>`,这对应于组合子 `<T>` 。因此,服务器可以理解接下来的内容将是类型为 `<T>` 的值。客户端在收到此查询的响应后,知道它必须收到类型为 `<T>` 的值,并据此反序列化响应。Vector User0x2d84d5f5getUsers#2d84d5f5 (Vector int) = Vector UserVector intVector User