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

  一、基本类型

  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. eclipse中注释常用关键字

    关键词列表: @author 作者名 @date 日期 @version 版本标识 @parameter 参数及其意义 @since 最早使用该方法/类/接口的JDK版本 @return 返回值 @t ...

  2. SpringMvc的运行流程

    一.先用文字描述 1.用户发送请求到DispatchServlet 2.DispatchServlet根据请求路径查询具体的Handler 3.HandlerMapping返回一个HandlerExc ...

  3. Boot Sector - Hello world

    1. code bits org 7c00h mov ax, cs mov ds, ax mov es, ax call DispStr jmp $ DispStr: mov ax, BootMess ...

  4. perl 正则表达式之匹配

    一.用m//进行匹配 上篇用双斜线的写法表示模式,事实上是m//的简写,所谓简写,就是当用双斜线作为定界符的时候,可有省略开头的m. 不使用简写的时候,可以使用任何定界符表示模式,m().m<& ...

  5. 【曝】苹果应用商店逾千款iOS应用存安全漏洞

    据国外网站Ibtimes报道,知名网络安全公司FireEye日前警告称,由于一款名为“JSPatch”.可帮助开发者修改应用程序的软件上存在安全漏洞,导致苹果应用商店内1000多款使用了该框架的iOS ...

  6. C# 输入法

    C# 输入法 虽说输入法不是什么新事物,各种语言版本都有,不过在C#不常见:这就会给人一种误会:C#不能做!其实C#能不能做呢,答案是肯定的--三种方式都行:IMM.TSF以及外挂式.IMM这种就是调 ...

  7. UVa1587 盒子

    前言 第一次刷题,ac的感觉真的很棒! 题目 题目 大意是说,输入6个面,判断是否是个长方体. 思路 根据长方体的特质来判断,比如说6个面中3个面是相互对应的,只有3条不同的边等等. 我就知道我肯定会 ...

  8. KoaHub.js可借助 Babel 编译稳定运行在 Node.js 环境上

    koahubjs KoaHub.js -- 基于 Koa.js 平台的 Node.js web 快速开发框架.可以直接在项目里使用 ES6/7(Generator Function, Class, A ...

  9. 使用Intellij Idea自定义MVC框架

    ---恢复内容开始--- 今天我学习了自定义一个简单的MVC框架,这个我们首先要知道什么是MVC框架! MVC框架: MVC全名是Model View Controller,是模型(model)-视图 ...

  10. ubuntu下python flask环境搭建

    ubuntu下python flask环境搭建 1. 安装pip sudo apt-get install python-dev pyhton-pip 2. 安装virtualenv sudo apt ...