Unit 2.0 (设计稿)
目的
- 简化、优化Unit的代码编写
- 可以按需动态加载插件
- 可以输出变量
- 可以输出引入的其他Unit
- 允许把多个Unit组装成包,可以自动下载安装相关依赖
Unit 2.0 的编码原则
-
每个
.tsl
文件都是一个Unit,无需专门设置Unit名称,以文件路径名来引入,支持相对路径。UnitA.tsl:
Main.tsl:
-
不需要定义Initialization和Finalization。
- Unit开头的代码就是Initialization
- Finalization 可以通过Unit的变量析构来实现。
-
不区分Interface和Implementation,默认全部输出,要控制输出可以使用
{$export}
-
按需加载插件,用
{$plugin}
来加载插件。A.tsl:
如果不指定文件名后缀会自动加上,例如:
文件名 操作系统 MyPlugin.dll Windows libMyPlugin.so Linux libMyPlugin.dylib macOS 会在
A.tsl
所在目录加载相应的动态库,并引入里面的函数。 如果有多个Unit都加载同一个插件,插件实际只加载一次。 -
如何既可以做Unit来引入使用又可以做主程序来运行?
用
{$main begin}
和{$main end}
来包裹作为主程序运行的代码。这样在引入时不会编译执行这些代码,只有作为主程序直接运行时才会编译执行。My.tsl:
这样在
uses "./My"
时不会打印run as main!
,TSL My.tsl
直接运行时才会打印run as main!
。这样方便TSL代码既可以做库用,也可以做工具用。
注意
{$main begin}
和{$main end}
包裹的代码在作为引入时并不会做编译,直接忽略。 所以不要在其中定义想要输出的内容。- TSL解释器可以考虑提供命令行参数允许直接执行某个Unit。
Unit的引入
-
用路径名引入。
-
查找路径,支持绝对路径和相对路径。
Unit的查找是非常快的,例如指定在d:\funcext
中查找,直接就可以通过d:\funcext\my\a.tsl
来定位。 -
只允许在文件头引入,其他地方的引入失去意义(作为兼容,此时允许unit 1.0的引入)。
-
不支持循环引入,例如不支持A引入B,而B又引入A。
-
将来可以支持一些引入的扩展,例如:
Unit的输出
-
默认全部输出。
-
{$export}
控制输出。 -
输出引入的其他Unit。
例如可以用
ui.tsl
来输出ui_*.tsl
:ui.tsl:
-
定义的类可以作为Unit的属性输出(同理,unit中定义的变量和函数也是unit的属性的一种)。
A.tsl: Type MyClass = class x := 1; y := 2; end; B.tsl: Uses "./A"; obj := new A.MyClass(); // 也可以继承 type ZClass = class(A.MyClass) z := 3; end;
由于在编译时已经知道是类了,我们还可以做到更多:
Type MyClass = class x := 1; y := 2; end; println("MyClass.classinfo={}", MyClass.Classinfo()); // 可以轻松区分类的方法和类实例的方法 obj := MyClass(); // 构造可以省略new obj := MyClass(x: 3, y: 4); // 甚至构造时就可以直接给成员变量赋值 v := MyClass.x; // 直接使用类的值,可以轻松和类实例的值区分开来
关于类的改进,会在后续的提案中给出。
Unit中定义的变量在其子函数中的可见性
-
子函数中如果赋值,都是定义局部变量。
-
如何解决局部变量可能和unit变量冲突的问题?
- 子函数中用
var
开头来强制声明成局部变量。
- 子函数中用
uplevel#0
来声明使用unit的变量。
uplevel#0
此时的寓意是在本文件的最高层(也就是unit层)中定义的变量。 - 子函数中用
-
为什么不使用global?
- 因为这个不是global的,只是unit的。
- global有global的问题,例如不知道应该在哪里来初始化它。
-
小结
- 子函数的变量不要和unit的变量重名。
- 在子函数中如果需要修改unit变量的值,用
uplevel#0
。
package
package包含多个unit。
package的组织
- 可以是一个目录,目录中包含
tpi.ini
文件。tpi.ini
文件的内容至少包含版本和依赖等信息。 - 可以是一个zip文件,相当于目录的打包。
package的使用
- tsl解释器可以直接运行打包的package,例如天软服务器可以执行打包好的代码。
- 命令行工具tpi可以上传、下载package和它的相关依赖。
兼容性
- Unit 2.0只查找
.tsl
文件,Unit 1.0只查找.tsf
文件,所以完全不会有冲突。 Unit(UnitName).UnitFunction
的方式只支持Unit 1.0,Unit 2.0已经在编译的时候就已经确定了调用,不需要这个模式。-
Unit 1.0所有行为都不变。
- 不能输出变量,只能输出函数。
-
可以混用Unit 2.0和Uint 1.0的引入,但不推荐这样使用。
.tsf
文件。
额外的好处
- 编译时就可以确定相关的变量、类定义和函数调用,所以运行时就没有额外的开销。
- 由于变量、类定义和函数调用在编译期间就已经确定了,所以在编译的时候就可以做很多优化的工作,例如inline;同时编译时也能做更多的检查,例如变量未初始化就引用、类或函数找不到等等,而不用等到运行时再报错。
- 为将来有可能直接编译成机器指令做准备。
Unit 2.0 的应用范例
获取公共配置信息
settings.tsl:
main.tsl:
设计原则和取舍
最简化原则,Less is more
-
如果用
.tsl
可以解决,不会引入其他文件名后缀,例如.tsf
,.tsu
,.tsp
, ....tsl
能解决吗?当然可以。PHP只有.php
,Python也只有.py
一个文件名后缀。
他山之石
不熟悉Python的人可能会认为在每个目录中必须要有__init__.py
才可以import其他.py
文件。
这个是不对的,即使没有__init__.py
,Python仍然会导入包。
如果可以在编译期间解决,就不要留到运行时
- 越早发现错误,错误带来的损失就越小。
把控制权交给真正写代码的开发人员(也就是TSL语言的用户)
- 用户想要把这个
.tsl
文件做Unit引入就引入,做主程序执行就执行。 - 不要限制用户这个
.tsl
只能执行,不能作为Unit引入;或者限制用户只能做Unit引入,不能直接执行。
强调便利性
人生苦短,我用....TSL!