本篇,来谈谈类型系统,以及部分与垃圾收集器相关的内容。

  一、基本类型

  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(二)的更多相关文章

  1. 脚本语言:Xmas(一)

    很偶然的一个想法,在从北京回成都的高铁上:我想要一个计算器.于是在火车上花了十来个小时,完成了一个模型:能够处理+-*/的优先级,以及"()",比如:1+(3+2)*4.这已是一年 ...

  2. 脚本语言:Xmas(三)

    自从将Xmas的GC换成现在的非迁移式的全局收集器后,最近几个月一直耗在Xmas上面:最明显的改变就是:更彻底地支持了面向对象.更强大的编译器. 所以,本文就来说说,真正的Xmas. 一.目标 一门语 ...

  3. 全栈工程师之路(二)—— JavaScript(网页前端脚本语言)

    javascript 是可以运行在网页前端的脚本语言,可以基于 html 之上实现更丰富的交互(网页内容的交互显示).异步回调.多线程.定时器.动画等. hello_world.html <ht ...

  4. InstallShield 脚本语言学习笔记

    InstallShield脚本语言是类似C语言,利用InstallShield的向导或模板都可以生成基本的脚本程序框架,可以在此基础上按自己的意愿进行修改和添加.     一.基本语法规则      ...

  5. JS脚本语言是什么意思?

    javascript,Javascript是一种浏览器端的脚本语言,用来在网页客户端处理与用户的交互,以及实现页面特效.比如提交表单前先验证数据合法性,减少服务器错误和压力.根据客户操作,给出一些提升 ...

  6. 使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  7. [Java面试九]脚本语言知识总结.

    核心内容概述 1.JavaScript加强,涉及到ECMAScript语法.BOM对象.DOM对象以及事件. 2.Ajax传统编程. 3.jQuery框架,九种选择器为核心学习内容 4.JQuery ...

  8. JS的脚本语言

    js的脚本语言全程javascript在网页里面使用的脚本语言:分类:1.嵌入网页里面2.在外部脚本标签可以写在网页的任何地方,但一般都写在网页的底部:<script type="te ...

  9. 9月12日JavaScript脚本语言

    JS脚本语言 JS脚本语言全称JavaScript,是网页里面使用的脚本语言,也是一门非常强大的语言. 一.基础语法 1.注释语法 单行注释:// 多行注释:/**/ 2.输出语法 ①alert(信息 ...

随机推荐

  1. HDU---Labyrinth

    Labyrinth Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  2. Java设计模式之《单例模式》及应用场景

    摘要: 原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6510196.html 所谓单例,指的就是单实例,有且仅有一个类实例,这个单例不应该 ...

  3. (十) Jquery的基本使用

    一.引入 注意:如果在index.js中有使用到JQuery.js 那么JQuery.js文件必须比index.js文件先引入,否则index.js文件中不能使用JQuery.js. <scri ...

  4. java开发之阿里云对象存储OSS和云数据库Memcache的使用

    web开发中标配:aliyun ECS(阿里云服务器),aliyun RDS(阿里云数据库),aliyun OSS(阿里云对象存储),aliyun Memcache(阿里云缓存数据库). 今天就介绍下 ...

  5. STC-单片机控制系统

    电源测量命令 60V开 FD02060001010100000060V关 FD02060002010100000060V保开 60V保关 -8KV开 -8KV关 FD02092802010201000 ...

  6. 给 Qt 添加模块

    添加 Qt 模块 QtCanvas3D 由于需要使用 Qt Quick 进行 3D 绘图,因此在网上找了一些资料. JS 绘制 3D 的有 ThreeJS 库,应该可以用于 QML.继续搜索,发现Qt ...

  7. CoreAnimation 图层几何学

    CoreAnimation 图层几何学 博客园MakeDown支持不佳,如有需要请进GitHub 图层几何所讲主要是有关图层的位置,尺寸等几何类属性. 布局 在UIView中与位置,尺寸有关的属性有 ...

  8. django进阶-3

    先看效果图: 登陆admin后的界面: 查看作者: 当然你也可以定制admin, 使界面更牛逼 数据库表结构: app01/models.py from django.db import models ...

  9. VAO VBO IBO大乱炖

    最近对程序中绘制卡顿的问题忍无可忍,终于决定下手处理了.程序涉及的绘制比较多,除了点.线.三角形.多边形.圆柱体之外,还有自组格式模型.开始想全部采用显示列表优化,毕竟效率最高,虽然显示列表存在编译之 ...

  10. MFC画笔作用域的问题

    今天发现了程序中的一个BUG.功能是在鼠标经过图形时,对图形进行加粗重绘.默认使用白色画刷.为防止白色背景下看不清,在白色背景下改用黑色画刷.代码如下 CPen* pOldPen;if (pDC-&g ...