脚本语言:Xmas(二)
本篇,来谈谈类型系统,以及部分与垃圾收集器相关的内容。
一、基本类型
Xmas的基本类型:Null、Boolean、Label、String、Ref、Function、Integer、Float、Decimal、Array、List、Set、Map、Object;14个,相对于其他的脚本语言是有些多的;但,这些类型都是“原子的”,少了其中一种,多少会有些不方便(但并非不完备,如Decimal可以代替所有算数类型;Map则可以可以替代所有容器类型)。如果,你看过上一篇,会发现有那么一点点的不同——一星期前,Xmas只有13种基本类型。
是的,【Label】是昨晚刚加入的(嗯,但经过了一段时间的思虑);其在Xmas代表了:一种编译期常量,或者说是可命名字符串常量的整数映射。加入的原因有二:
1、Object和Struct函数,缺少一种合适的参数类型:Object{ field : value} -> Object("field", value),这是之前的做法(当然,这个转换由编译器完成);但是,当我们想要纯手写时,每个字段的名字都使用字符串来代替,是一种不自然的方式;而非更自然的:Object(field, value)。这里【field】便是一个Label。
2、为了性能,有了Label,我们可以在运行时,大量使用Label来代替字符串;这将会有一个可观的回报(其实,也不会太大;部分字符串,是在编译器创建的,并且是共享的)。
唯一需要纠结的是:如何创建/申明一个Label?
其他语言中典型的有 :field(Ruby)、‘field(Lisp);很丑,说真的。本来Ruby的“:”还可以接受,但Xmas中的“:”已经有了几种用途了,再加入一种会给编译器带来一定的负担(有歧义的语法,处理需要费一些脑细胞)。
然后,想到了Xmas中的“统一的类型创建”:new Type() / new Type{}。后者是统一初始化的语法(比如Map可以:new Map{ val1 : 12};而不可以:new Map(val : 12));那么,Label也是需要支持这种形式的创建,那么其便可以这样:
var = new field;//创建一个Label(“field”),没有()/{}
于是便有了下面的 Object创建方式:
var0 = new Object{ field : value}; //统一初始化 var1 = new Object(new field, value); //手动函数调用
这比使用字符串的方式干净一些;当然,也有妥协,毕竟Label本身和其他的字面量(变量、参数、函数名)形式上没有任何的区别;而,我们却必须要去区别(否则,必须要使用类似统一初始化的附属结构来区分)
需要注意的是,new本身是带有在创建对象的意思,在Xmas中也没有例外;但Label是个例外,其是在编译期创建的,所以并没有任何额外的消耗。
二、创建
Xmas中每种类型都有至少3种创建的方式(除了前面的Label,其无法无歧义地申明):字面量、new Type()、new Type{}、Type()。
基本类型一般便有4中(至少形式上):
var0 = ; var1 = ); var2 = }; var3 = );
其中让人困惑的是:new 是否有必要? 有:
1、只有加上new,才能够使用统一的初始化方式(这对于Map和Object很重要的方式)。
2、只有加上new,编译器才能够知道:你是在创建一个对象;才能够执行相应的额外处理(后面会讲)。
3、让你自己和别人知道,这句代码是在创建一个对象。
至于为什么不让编译器自己去识别对象的创建(如:var = Integer(12));可惜的是这是可不能的:
;}; );
Xmas中对象的创建函数并不是关键字(也不可能,如自定义类);所以,上面的这段代码,编译器并不知道“Integer(12)”是创建一个整数,还是调用一个函数——实际上,正确的是函数调用。这就是,支持函数式编程的代价之一:函数调用的静态性没有了,因为任何变量都可能是一个函数。
所以,new便有了存在的必要:其强制声明一个对象的创建,其后面的是该类型的名字。当然,有了这个信息后(确定是否是对象创建),编译器便有了更多可以做的事情:自定义类型的创建优化。
本质上,一个自定义类型的创建需要,下面完整的四个步骤:
//1、创建一个空对象(类似于C++的分配内存) obj = new Object(); //这是一个函数调用,可以不加“new” //2、设置类信息(等价于C++的设置虚函数表) obj.class = new Class("AClass"); //这也是一个函数调用,并不需要“new” //3、调用自定义类的构造函数 obj.__init(); //这个字段是新加的,其保存了构造函数 //4、设置对象的final属性 try_final(obj); //这个比较复杂,后面会讲(涉及到GC)
这也是一个使用Reflection(反射)的完整案例;当然,这是手动的方式,也并非推荐的方法。有了new作为声明后,编译器可以将所有的这些操作,直接映射到一个系统函数的调用上:
obj = class("AClass"); //使用了和关键字“class”同名的函数
因为和关键字同名,所以代码中是不可以直接调用的;当然,你可以这样:
func = new Function("class"); //创建一个函数,通过函数名 obj = func("AClass");
当然,这不是重点;重点在于,如果不使用new来告诉编译器“这是在创建对象”;而是用:
obj = AClass(); //直接调用类的创建函数(由编译器生成的)
当然,如果前面没有一个名为“AClass”的变量的情形;编译器是可以推断出这是一次对象创建的,只是我懒;而且这并不是推荐的做法。更关键的是,为了识别出“这是一次对象创建”,编译器需要知道额外的信息:知道有这么一个类型,名叫“AClass”。而非,可以推迟一些时间。
三、面向对象
我们都知道,本质上面向对象,只是对象的内存布局如何设置(父类和子类),以及虚函数表如何实现。
在前面,我们已经知道了Xmas中的虚函数表:一个名为“class”的字段——其中放置了自定义类型的所有信息(成员函数,类型信息)。那么,【.class】中究竟有什么?
class AClass :Xmas{ function AClass(){ this.Xmas(); //调用父类的构造函数 } function func(){} }
上面的定义,究竟会生成怎样的class?
class: ....父类Xmas的东西.... __init = Function : AClass.AClass //AClass的构造函数 __type = Function : AClass //编译器生成的创建函数(其内部调用:class("AClass");) __name = String : “AClass” //类型名字 __final = Boolean : true/false //类型的final属性 AClass = Function : AClass func = Function : AClass.func //自定义的函数
很多东西是显而易见的:新加的__init是为使用反射创建对象时的方便,否则就需要如下的方式:
....... obj.constructor = get_value(obj.class, "AClass"); //批量反射创建时,我们可能只知道字符串形式的类型名称 obj.constructor(); ....... // 以上对应了: obj.AClass();
关于虚函数表这个东西,只要知道有这么一个东西;便也能够很自然构造出对应的。但另一个问题:对象的内存布局,一直是面向对象的一个大问题。除了C++其他语言使用interface来解决这个。是的,只要禁止了多重继承,这个问题便变得很简单。
对此Xmas,并没有给出合适的方案:简单的成员覆盖——父类的重名字段和函数,都只是简单地覆盖了,不可访问;即使在父类的空间也不可能。原因,很简单我个人暂时不会用到面向对象的那些功能;所以,也不会耗费精力去做.......懒癌,晚期。
当然,Xmas不止这些;通过反射,我们可以很轻易地构造出一个对象;但,或许我们可以这样:
obj = new Object(); obj.; //这算是恶作剧?
Xmas并不会让这段代码得以运行;在执行到“obj.class = 12;”时,会检测【右值】是否真的是一个class。这意味着,我们不能够通过反射,手动地创建出一个新的类型(在几天前,还是可以的)。Xmas限制了反射的能力——变成“只读”;但同时,也禁止了创建出无效对象的可能——“class”字段是一个内部字段,只能够用来放置类型信息。
需要额外一提的是,Xmas的类型信息是按需生成的——在第一次创建该自定义类型时,才构建对应的类型信息。
四、final属性
在Xmas中,一个对象有三重意义上的【不可变】:
1、除了容器类型(Array、List、Set、Map、Object),其他类型都是【不可变的】。
2、容器类型,拥有一个可设置的final属性;而且只可设置一次:将其设置为【不可变】(默认是可变的)。
3、自定义类型,在继承了第二点的同时(因为其使用Object作为容器);额外有一个由编译器生成的final属性(即:class.__final)。
对于第一点,很容易理解:类似Java中String,其是不可变的,对其任何的操作都将创建一个新的String。同样,Xmas中的不可变类型,也是如此——你没有任何办法改变一个Integer内部的值(这很有函数式风格)。这点是有两面性的:
1、不可变性,意味着任何的计算都将创建新的对象;而非复用,这无疑加重了GC的负担(最典型的是循环中的游标,循环1万次,需要创建1万个游标)。
2、不可变性,意味着对象复用没有任何的负担;一个String可以在整个代码中到处复用,而不用担心其被改变;相反,这减少了GC的负担(但减少的并不多)。
这对于编译器而言,其意义更大:编译时,可以放心地复用任何对象(编译期只会创建简单的不可变对象),而不用担心被污染。
而第二点,类似C++的const修饰:所修饰的对象不可变,但其成员中指针所指向的对象,并不能限制。就一门语言而言,这样的属性,并没有必要的存在(许多语言都没有);而且这个不可变属性,是绝对的——一旦设置了【不可变】,不可更改,该对象永远都不再可变!
list = , 'hh', 2.2}; final(list); //设置对象为不可变 list[] = //错误!运行时,会抛出异常
第三点,是继承了第二点的概念,但有所扩展:一个自定义对象创建后,其会被自动地设置final属性(根据class.__final中的值):
try_final(obj);
// 等价于
if(obj.__final){
final(obj);
}
其目的同样是,为了完成相同的目的:设置对象为【不可变】,自动地。
所以,唯一的疑问就是:class.__final从何而来?
class ClassA{ function ClassA(){ ; } } class ClassB: ClassA{ function ClassB(){} function setValue(val){ this.value = val; } }
其中ClassA.class._final = true,而ClassB.class.__final = false。为什么? 就该字段的意义而言,其意味着:该类型的实例,是否允许被更改。Xmas,有这么一个简单的逻辑——如果类定义中,没有任何函数更改【this】的内容;那么,该类型便是不可变的。
所以,想要定义一个可变的类,是需要对应的【setXXX】系类函数的。 所以,ClassB有一个函数改变了this的内容,因此是可变的。当然,构造函数内部的任何操作,并不影响final属性——因为,构造函数只会被调用一次。
五、GC
为什么会有垃圾收集器出现在这里? 因为,【final】本身是为了GC存在的。
前面有讲过Xmas的GC是基于标记的分代式收集器;因此,为了完成真正意义上的分代:进行young GC时,并不会去标记老年代的对象(尽可能地)。Xmas提供final抽象:一个final对象其直接所持有的对象,其所处的分代并不会,从老年代降到年轻代,只会从年轻代升到老年代。
这句话很是抽象;其大致上代表了:
val = ; obj = new Object{ val : val}; final(obj); .....gc...... 、obj,val同时进入老年代 、obj为final,那么obj不可能有指向年轻代的引用(任何时刻) .....young gc..... //因为obj为老年代,且没有指向年轻代的引用,所以不必扫描
就结果而言;在有大规模的存活对象时(多数已晋升到老年代;因为年轻不可能有大量的对象);进行young GC时,其所需要扫描的对象比例在10:1~1000:1之间(Xmas得到的结果)。这样的效果是卓越的,GC总时间减少了80%-90%。
当然,这样的假设只有在:拥有大量不可变对象的背景下,才会成立。Xmas为此付出大量努力。比如对象的创建有两种方式:new Object{}、new Struct{}。区别在于,Struct创建的对象,其被设置了不可变。Xmas通过GC免去了内存管理的烦恼;但同样地也给予了一定的干涉GC的能力,当然,或者说是辅助GC的更好地工作(Xmas的自定义类的final属性,是自动化辅助GC的能力;并不需要手动干预,也不能)。
至于原因嘛?因为,GC并非Xmas的一个子模块,而是另一个完全独立的系统;其存在并非只是为了Xmas(大部分是),还有我的库中的其他模块。
PS:否则需要另外的技术来避免大量的老年代对象被扫描,而且其有着额外的代价。
六、类型系统
Xmas有一个获取对象类型的关键字函数:typeof。其返回的东西,很有意思:函数。在最早的版本中,返回的是字符串(当然,现在的话,也可以考虑Label)。
但返回函数,能够更优雅地解决问题。
obj = new ClassA(); if(typeof(obj) == ClassA){ print('he he'); }
通过返回该类型的创建函数;我们可以写出更直白和方便的代码:不仅免除了字符串的引号,而且可以这样:
var = typeof(obj)("I'm a param");
上面的代码创建了一个obj类型的新的实例,即使我们并不知道obj的类型是什么(函数作为第一值类,还有很有价值的)。
脚本语言:Xmas(二)的更多相关文章
- 脚本语言:Xmas(一)
很偶然的一个想法,在从北京回成都的高铁上:我想要一个计算器.于是在火车上花了十来个小时,完成了一个模型:能够处理+-*/的优先级,以及"()",比如:1+(3+2)*4.这已是一年 ...
- 脚本语言:Xmas(三)
自从将Xmas的GC换成现在的非迁移式的全局收集器后,最近几个月一直耗在Xmas上面:最明显的改变就是:更彻底地支持了面向对象.更强大的编译器. 所以,本文就来说说,真正的Xmas. 一.目标 一门语 ...
- 全栈工程师之路(二)—— JavaScript(网页前端脚本语言)
javascript 是可以运行在网页前端的脚本语言,可以基于 html 之上实现更丰富的交互(网页内容的交互显示).异步回调.多线程.定时器.动画等. hello_world.html <ht ...
- InstallShield 脚本语言学习笔记
InstallShield脚本语言是类似C语言,利用InstallShield的向导或模板都可以生成基本的脚本程序框架,可以在此基础上按自己的意愿进行修改和添加. 一.基本语法规则 ...
- JS脚本语言是什么意思?
javascript,Javascript是一种浏览器端的脚本语言,用来在网页客户端处理与用户的交互,以及实现页面特效.比如提交表单前先验证数据合法性,减少服务器错误和压力.根据客户操作,给出一些提升 ...
- 使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍
一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...
- [Java面试九]脚本语言知识总结.
核心内容概述 1.JavaScript加强,涉及到ECMAScript语法.BOM对象.DOM对象以及事件. 2.Ajax传统编程. 3.jQuery框架,九种选择器为核心学习内容 4.JQuery ...
- JS的脚本语言
js的脚本语言全程javascript在网页里面使用的脚本语言:分类:1.嵌入网页里面2.在外部脚本标签可以写在网页的任何地方,但一般都写在网页的底部:<script type="te ...
- 9月12日JavaScript脚本语言
JS脚本语言 JS脚本语言全称JavaScript,是网页里面使用的脚本语言,也是一门非常强大的语言. 一.基础语法 1.注释语法 单行注释:// 多行注释:/**/ 2.输出语法 ①alert(信息 ...
随机推荐
- struts2中Action到底是什么,怎么理解
struts2中Action到底是什么,怎么理解 1.配置完web.xml2.创建视图页面login.jsp3.创建业务控制器LoginAction类(解释说:创建业务控制器LoginAction类, ...
- Ubuntu16.04下搭建LAMP环境
前期准备sudo apt-get update # 获取最新资源包sudo apt-get upgrade # 本机软件全部更新sudo apt-get d ...
- WebGIS中自定义互联网地图局部注记的一种方案
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1. 前言 实际项目中我们经常会遇到这样一种场景:地图底图可能是互 ...
- ThinkPhp框架的数据库操作(查询)
TP框架有一套自己的数据库操作的代码,包括数据库的增.删.改.查.本文主要讲解TP框架的数据库查询操作. 找到入口文件的控制器: 我这里的入口文件是Show文件夹下的控制器. 打开Login控制器. ...
- 双系统删除Ubuntu后出现grub界面而无法正常启动Windows系统的解决方法
第一次安装双系统的时候由于不怎么会弄,设置了ubuntu引导windows,这种方法是非常不推荐的,因为当ubuntu出现问题或者是当你不再使用ubuntu的时候,删除ubuntu就会成为一个很麻烦的 ...
- 把GIF背景变透明
准备软件: 1.Ps cs4 2.QuickTime Player 7.74 开始: 1. 2.弹出文件选择框,但是发现不能选择GIF格式. 3.没关系,在文件名框输入*.*回车,就发现可以选择GIF ...
- Authorization user to use specifical database
DENY VIEW any DATABASE to PUBLIC;ALTER AUTHORIZATION ON DATABASE::Best TO Best
- ie旋转滤镜Matrix
旋转一个元素算是一个比较常见的需求了吧,在支持CSS3的浏览器中可以使用transform很容易地实现,这里有介绍:http://www.css88.com/archives/2168,这里有演示ht ...
- shell中的readonly
readonly用来定义只读变量,一旦使用readonly定义的变量在脚本中就不能更改 测试脚本 #!/bin/sh readonly a='haha' echo a a='xixi' //更改变量的 ...
- oracle server config
安装oracle数据库软件 database/runInstaller; ##执行该程序开始安装 创建数据库 在oracle用户的图形界面oracle用户中 新开启一个终端,直接输入命令dbca会弹出 ...