详解半静态语言原理及价值

开发 后端
什么是半静态语言呢?本文将由阿里巴巴技术部中文站架构师何坤(Raymond He)系统地向大家介绍这篇《详解半静态语言原理及价值》。

51CTO编者按:我们曾经在《基于JVM的语言正在开始流行》一文中介绍过全新的基于JVM的脚本语言是Groovy和Scala。这两门语言现在都越来越流行,不同的是,Groovy是动态脚本语言,而是Scala是静态语言,那什么是半静态语言呢?本文将由阿里巴巴技术部中文站架构师何坤(Raymond He)系统地向大家介绍这篇《详解半静态语言原理及价值》。

引言

动态类型语言在企业开发和互联网领域应用广泛,如Ruby ,Velocity, Python等。 动态类型语言在运行时进行类型推断,以解释方式执行,修改即生效,开发灵活性高;而静态类型语言(如:Java,C/C+/C++) 在执行前做类型检查,需要编译运行,对于互联网前端开发不够灵活。

因此,许多大型互联网站选择 Freemarker, Velocity这样的动态模板语言作为页面开发语言,在一定程度上满足了前端敏捷开发的需求。

然而,对于大型电子商务网站,不仅具有一般互联网需求频繁变更的特点,更显著特点则是业务繁多,业务模型和业务关系复杂。 因此,在此类应用开发中,Velocity 的开发也遇到了一些的问题。

前端模板开发问题

1、降低软件质量

Velocity是弱类型动态语言,运行时才能检查出类型错误。由于动态类型等特点,有的错误在遇到特定参数时,才能激发执行路径,软件质量不能很好的保证。

2、测试成本高

由于无法像静态语言一样,在运行前进行类型检查,因此软件的测试周期长,测试成本高。

3、开发不敏捷

缺乏一些敏捷开发功能如IDE内实时验证、代码提示、代码重构等。虽然能修改即生效,但对于企业级开发,效率较低。

4、维护性差

对于一个大型系统,在重构业务模型(Java Model)或代码时, 无法知道哪些Velocity模板会受到影响;常常需要花费大量时间搜索相关模板,然后修改、测试。例如:笔者所在公司的一个基础产品升级,由于受影响模板众多,重构复杂,项目评估达上千人日。

这些动态语言天生的缺点在企业级和大型网站应用中非常突出,严重的影响了开发质量和开发效率。因此,在技术上亟待一种新的高质量、高效率的开发技术。

静态语言的优势

综合考虑后,我们发现动态类型语言(Dynamic Language)“解释执行方式和修改即生效”的最大优点仍是不能舍弃的。必须从问题出发,找到一条平滑的线路来解决问题。

遇到上述问题时,我们不由自主的会赞美Java的优点:

1、静态语法和静态类型实时检查。

如果赋值类型不匹配,方法不存在,参数类型错误等信息能马上在IDE中显示;

2、代码提示:

调用属性,方法时能代码提示,开发非常高效;

3、代码热链接:

通过变量和类名热链接到对应的Java类;

4、代码重构:

修改一个Java类时,受影响的Java代码会被实时重新验证,马上会显示红色的错误; 更强大的是重构,对Java类,方法敏性重命名,会自动修改所有相关代码中对它的引用。

Java等静态类型语言的这些优势就是解决问题的方向。那为什么动态语言不能做到这些呢? 原因在于动态语言的根本特点是变量无类型(即弱类型特点),类型在运行时推断,这使得它无法在开发阶段进行类型检查。

那如何将动态语言和静态语言的优点结合呢?答案就是半静态语言。

Phone Club——51CTO移动开发线下技术沙龙

活动日期:12月19日
本期主题:Android应用开发技术进阶
地点:创新工场 北京市海淀区北四环西路66号第三极大厦B座18层
演讲讲师:王明礼(创新工场) 范怀宇(网易)

#p#

半静态语言(Semi-Static Language)

1 定义

半静态语言,严格说应该是静态化类型的动态语言(Statically Typed Dynamic Language)。它是这样一种语言:以静态方式开发,以解释方式执行;通过变量显式声明或隐式声明,运行前可对变量类型进行推断和验证。

静态语言,动态语言和半静态语言的特点对比分析如下:

语言类型

优点

缺点

举例

适用场景

Static Language

强类型,运行前类型检查,程序健壮

对Java等支持反射的语言,可实现代码提示,重构等敏捷开发特性

需编译运行,发布慢

无法快速响应需求变化

Java

C/C++

企业级后端开发

大型互联网后端开发

Dynamic Language

灵活性高,修改即生效

快速响应需求变化

弱类型,运行时类型检查,程序不健壮,测试成本高

PHP

Ruby

Velocity

业务简单的小型互联网前端开发

Semi-Static Laguange

开发时(Devtime)强类型,程序健壮

运行时(Runtime)弱类型,修改即生效,快速响应需求变化

   

业务复杂的企业级开发和大型互联网前端开发

半静态语言集合了静态语言和动态语言的优点,更适合企业级和大型互联网开发,例如:电子商务,ERP,金融,保险等。

2 技术原理

2.1 范例

为了实现目标,需要在动态类型语言基础上,引入变量声明技术。因此本质上,半静态语言也是一种声明式语言(Declarative Language), 这一点与静态类型语言一样。

以Velocity模板语言为例:

当前Velocity Template编程代码范例如下:

  1. [Code 1] showBuyProducts.vm  
  2.  
  3. <HTML>> 
  4. Hello $customer.Name  
  5. <table>> 
  6. #foreach( $product in $buyingProducts )  
  7.    Buy: $product.Name,  Price: $product.Price,   
  8. #end  
  9. table>> 

     

  10.  

该模板执行后,HTML页面上将用 $customer.Name 显示“客户名称”,循环显示该客户购买的每个产品的名称和价格。在Velocity中,运行时通过Velocity Context传递变量$customer和 $buyingProducts,而开发时这两个变量是未定型的(Untyped,或者说都是Object类型)。

为了实现静态化开发,引入变量声明,在模板顶部对变量$customer,$buyingProducts进行显式类型声明。变量声明指令为“##$”。

格式为:

  1. ##$ <Type> <var1[,var2[,[…]]]> 

带有变量声明的半静态模板代码为:

  1. [Code 2] showBuyProducts_static.vm  
  2.  
  3. ##$ com.abc.crm.Customer customer  
  4. ##$ com.abc.saling.Product product  
  5. ##$ List<Product> buyingProducts  
  6.  
  7. <HTML> 
  8. Hello $customer.Name  
  9. <table> 
  10. #foreach( $product in $buyingProducts )  
  11.    Buy: $product.Name,  Price: $product.Price,   
  12. #end  
  13. table> 
  14.  

上述代码中,指定了变量customer的类型为 com.abc.crm.Customer,变量buyingProducts 的类型为Product泛型集合。由于 "##"是Velocity的注释指令,因此 "##$" 在Velocity Engine解析(Parse)和渲染(Render)时不会与现有语法冲突,Velocity引擎能正常执行,从而保证了兼容性。

Phone Club——51CTO移动开发线下技术沙龙

活动日期:12月19日
本期主题:Android应用开发技术进阶
地点:创新工场 北京市海淀区北四环西路66号第三极大厦B座18层
演讲讲师:王明礼(创新工场) 范怀宇(网易)

#p#

2.2 动态语言一阶段模型

在动态类型语言中,只有一个运行时(Run Time)阶段,运行阶段由解释器(Intepreter)来对源代码进行解析(Parsing)、执行(Evaluation)产生执行结果。过程如下:

Run Time阶段过程

由于动态语言无类型的特点,在解析步骤中产生的抽象语法树(Abstract Syntax Tree,AST)所有变量被存储为统一的类型,例如JavaScript,Velocity中变量都作为 Object 类型。在执行步骤,一般由类型推断系统(Type Inference System)负责根据变量的实际值动态判断变量的类型,并判断函数、方法或属性调用是否正确,由解释器进行执行或计算,从而产生结果。

2.3 半静态语言两阶段模型

而半静态语言,分开发时(Develop Time)和运行时(Run Time)两个阶段,两个阶段互不干扰。

1、开发时阶段。

开发时进行类型检查。一个“编译器”,更严格说是类型化解析器(Typing Parser)负责对源代码进行解析和类型检查,然后输出检查结果。“变量声明”是类型检查的必要条件。检查结果包含类型检查失败的错误信息和警告信息,类似于 Java编译时的错误信息。

与静态类型语言不同,此编译器不输出机器代码或字节码,只输出类型检查错误信息。

2、运行时阶段。

此阶段中,源代码仍由解释器以解释方式执行,同动态语言的解释执行过程。

半静态语言的两阶段模型如下图所示:

半静态语言的两阶段模型

需要指出的是,运行时阶段仍采用无类型解析器(Untyping Parser), 是一个类型推断系统。而开发时采用的是一个新的类型化解析器(Typing Parser), 是一个类型检查系统(Type Checking System)。

2.4 开发流程

半静态语言的开发流程涉及5个步骤:

1、编码

2、编译(类型检查).

半静态语言的编译与静态类型语言很不相同,它的编译只进行类型检查,不产生机器码或字节码。因此,半静态语言的编译可以称为“检查”(Checking).

在这个步骤中,如果代码存在类型错误(Error),编译失败,那么你必须退回到步骤1)修改代码bug,直到代码编译正确。

编译过程还可以产生警告(Warning),程序员可以有选择的忽略。

3、测试

QA 执行功能测试,集成测试和系统测试。

如果测试失败,必须退回到步骤 1)。

4、发布

将代码发布到生产环境

5、执行

最终用户访问用半静态语言开发的应用功能。

开发流程

从上面的开发流程可见, 开发时阶段覆盖了步骤 1)、2), 运行时阶段覆盖了步骤 3)、4)、5).

为了保证只有编译合法的半静态语言程序在生产环境运行,需要有以下两条约束规则来保证:

1、代码编译合法后,才能提交到测试阶段;

2、测试正确的代码才能发布上线。

由于半静态语言仍用解析器运行,理论上代码仍具有修改即生效的特点。但从软件质量保证角度,这个缺点应该规避。因此上线后的代码不允许未经编译、测试的随意修改。

Phone Club——51CTO移动开发线下技术沙龙

活动日期:12月19日
本期主题:Android应用开发技术进阶
地点:创新工场 北京市海淀区北四环西路66号第三极大厦B座18层
演讲讲师:王明礼(创新工场) 范怀宇(网易)

#p#

2.5 类型检查系统和原理

半静态语言的类型检查系统中的核心组件编译器Compiler(或称为Checker),它本质上是一个类型化解析器。编译时,该系统采用类型检查算法(Type Checking Algorithm);而在运行时阶段,仍由解释器执行代码,采用类型推断算法(Type Inference Algorithm)。

半静态语言的类型检查基本原理是,根据变量声明对源码进行解析、类型检查和语义检查,输出检查结果。这个系统中类型检查系统的基本原理如下图所示:

类型检查系统的基本原理

我们使用一个命令行工具 vmcheck 来编译半静态语言代码。格式为:

  1. Format: vmcheck templateFile  
  2.  

以前面的声明式Velocity源码为例,类型检查系统包含以下几个基本规则和检查点:

1、变量是否声明;

如果变量 $customer 未声明,编译错误如下:

  1. Error: line:2,column:7,variable $customer not declared !  
  2.  

2、JavaBean的属性和方法是否存在

如果com.alibaba.saling.Customer类没有属性 'Name' , 编译错误如下:

  1. Error: line:2, column:7, property 'Name' not found for $customer.  
  2.  

如果com.alibaba.utils.CurrencyUtil 类没有方法 'convert' , 编译错误如下:

  1. Error: line:6, column:22, method 'convert' not found for $currencyUtil.  
  2.  

3、方法调用的参数匹配;

3.1) 如果这样调用 'convert' 方法:

  1. $currencyUtil.convert()  
  2.  

则产生如下编译错误信息:

  1. Error: line:6, column:22, insufficient parameters for  method call 'convert' .  
  2.  

3.2) 如果这样调用 'convert' 方法

  1. $currencyUtil.convert( $customer , "##.##" )  
  2.  

则产生编译错误信息:

  1. Error: line:6, column:22, parameter type mismatched of $customer for method call 'convert' , Double is required.  
  2.  

4、特定语句的类型匹配,如条件,循环语句:

如果有下面的复制语句调用

  1. #set( $customer.Name = $product.Price)  
  2.  

则产生编译错误信息:

  1. Error: line:11, column:5, type mismatched of assignment statement.  
  2.  

'if', 'foreach' 等语句使用的类型匹配规则类似。这与Java等强类型语言一样。

集合泛型的类型匹配

对于Java语言,JDK5+支持泛型特性。因此,类型检查也需支持泛型。对于以下代码

  1. ##$ List buyingProducts  
  2. $buyingProducts.add( $customer)  
  3.  

编译错误如下:

  1. Error: line:12, column:5, parameter type mismatched of $buyingProducts for method call 'add' , 'com.alibaba.saling.Product' is required.   
  2. As for the previous Velocity code snippet [Code 1], after executing 'vcheck' command on console,  
  3.  

Phone Club——51CTO移动开发线下技术沙龙

活动日期:12月19日
本期主题:Android应用开发技术进阶
地点:创新工场 北京市海淀区北四环西路66号第三极大厦B座18层
演讲讲师:王明礼(创新工场) 范怀宇(网易)

#p#

3 变量声明

变量声明就是对变量的类型进行声明。变量声明根据放置的地点分为两种,显示声明(Explicit Declaration)和隐式声明(Implicit Declaration)。

1、显式声明

显式声明采用特殊指令(Directive)或语句(Statement),在源码中对变量进行类型声明。

显式声明通常的格式为:

  1.      
  2.  

为了保持与运行时解释器的兼容性,我们引入一种“基于注释的扩展声明指令”技术。以Velocity模板语言(VTL)为例, 在Velocity注释指令“##”上扩展“##$”指令用于变量声明。如下例所示:

  1. [Code 3] showBuyProducts_static.vm  
  2.  
  3. ##$ com.abc.crm.Customer customer  
  4. ##$ List buyingProducts  
  5. ##$ String flag, sss, abc  
  6.  

对于其他动态类型语言,同样使用“基于注释的扩展声明指令”来实现兼容性的半静态语言。

Language

Comment Instruction

S2L Declaration Instruction

Velocity

##

##$

Javascript

//

//$

Ruby

#

#$

Python

#

#$

2、隐式声明

隐式声明不用在源码中编写声明语句,而从配置文件或其他地方分析变量声明。例如,使用Velocity进行Web App开发时,如果需要直接频繁操作request,response,session等Servlet容器对象,编译器可以将它们作为内置变量,使用隐式声明。如下表所示:

Built-in variable

Type

request

HttpServletRequest

response

HttpServletResponse

session

HttpSession

application

ServletContext

以下代码使用隐式声明变量 request, session,

  1.  
  2.  
  3. Hello, $request.getParameter("username") ! 

     

     
  4. Your logged in at $session.getAttribute("loginTime") last time.  
  5. body> 

这段代码看起来,对现有Velocity语法没有任何扩展. 但实际上,在编译时,编译器使用内置变量对源码进行类型检查。

如果编写了一段错误的调用,例如:

  1. $session.getParameter("loginTime")  
  2.  

则编译器输出一条“方法不存在的”错误信息:

  1. Error: line:12, column:5, method 'getParameter' not found for $session!.  
  2.  

Phone Club——51CTO移动开发线下技术沙龙

活动日期:12月19日
本期主题:Android应用开发技术进阶
地点:创新工场 北京市海淀区北四环西路66号第三极大厦B座18层
演讲讲师:王明礼(创新工场) 范怀宇(网易)

#p#

4 语法约束

半静态语言基于某种动态类型语言进行实现,但它在语法语义上更接近与静态类型语言。在这两个端点,存在一些矛盾的地方,比如:变量动态定型,ducking type等。因此,半静态语言需要有语法约束:

1、变量先声明,后使用

2、变量在作用域scope内置能声明为一个类型;

3、禁止Ducking type 也就是多, 动态语言的无继承多态特性不允许使用,因为这与静态类型系统是冲突的。

如果违反这几个规则,编译器会产生相应的编译错误。以ducking type 为例(Ruby支持,而Velocity等Java系列脚本不支持), 如果尝试访问一个不存在的方法,则会产生下面的错误。

  1. Error: line:12, column:5, method 'quack' not found for $dog.  
  2.  

而在Ruby中,只要 dog 存在 quack 方法,代码运行是正确的。

5 半静态语言组成模型和实现方式

半静态语言本质上是动态语言思想和静态语言思想的结合的产物。一种基本的半静态语言实现,核心功能是在运行前进行类型检查和语义检查。其组件集合 SS包括:

1、一种静态类型语言S,S以编译方式运行;

2、一种以S语言为基础的动态类型语言 D。D以解释方式由P执行,解释器 P 由S 编写;

3、在语言D的语法集合上扩展变量声明语法,新语法集合名为 SD ;

4、用语言S对解释器P进行扩展,实现 SD 的类型编译器C;

5、开发时,遵循SD语法集合的代码由 C 进行类型检查;

6、运行时,遵循SD语法集合的代码由 P 进行解释执行。

因此,新的半静态语言SS是基本组成是:新语法集合SD和类型编译器C.

SS = SD + C

SS = SD + C

举例:

Java 是一种静态类型语言,运行前进行编译和类型检查;

Velocity是一种基于Java的动态模板语言,通过 Velocity Engine以解释方式运行;

基于Velocity实现半静态语言的方式为:为Velocity基本语法增加变量声明指令(语句),基于Velocity 解释器 实现类型编译器 ,负责在开发时对模板进行类型检查。

实践中,Java体系的动态类型语言一般与Java语言天生的结合使用,应用广泛。以它们为基础,很容易通过扩展方式实现类型编译器,进而实现半静态语言。 例如Freemarker,Groovy,JRuby,Bean Shell等。其他动态类型语言也可以基于此原理设计半静态语言,如:Python,Ruby。

Phone Club——51CTO移动开发线下技术沙龙

活动日期:12月19日
本期主题:Android应用开发技术进阶
地点:创新工场 北京市海淀区北四环西路66号第三极大厦B座18层
演讲讲师:王明礼(创新工场) 范怀宇(网易)

#p#

IDE敏捷开发(Agile Development in IDE)

对于Velocity,Freemarker这类动态类型语言,它们基于Java等强类型语言,在模板内能直接操作传入的Java对象。由于Java等语言有反射(Reflection)机制。因而,除了静态类型检查的基本功能,可以在类型检查和反射技术基础上,实现一系列IDE敏捷开发功能。包括:

1、代码提示:编辑器内的Java对象的属性,方法代码提示;

2、参数提示:编辑器内的Java对象的方法参数提示;

3、全量构建和增量构建:Java类修改对相关Velocity模板的增量检查;

4、代码重构:修改Java属性或方法名称,自动批量修改相关模板中所有对应类型的JavaBean属性或方法名称。

其中3),4) 功能对于大型系统的维护和重构价值尤为明显。以上这些敏捷开发功能可独立实现或结合集成开发环境(IDE)如Eclipse插件来实现。

结论

通过以上分析可见,半静态化语言结合了静态语言和动态语言的优点,能很好的解决动态语言编程的开发质量和开发效率问题。半静态化语言保留了动态语言的灵活性优点,同时达到了静态语言在开发时强类型检查优势,能有效提升程序健壮性,减低测试复杂性和测试成本。通过与IDE结合,实现代码提示,代码重构等敏捷开发功能,有效提升动态语言的开发效率。在企业级应用和互联网应用开发中有着良好的应用价值。

Phone Club——51CTO移动开发线下技术沙龙

活动日期:12月19日
本期主题:Android应用开发技术进阶
地点:创新工场 北京市海淀区北四环西路66号第三极大厦B座18层
演讲讲师:王明礼(创新工场) 范怀宇(网易)

【编辑推荐】

  1. 5月编程语言排行榜:动态语言的前世今生
  2. Python等动态语言企业应用面面观
  3. JVM上的动态语言 各大巨头的新宠
  4. 开发团队裁至一人 微软或放弃.NET动态语言开发计划
  5. 简单介绍VB.NET C#区别和动态语言
责任编辑:张攀 来源: infoq
相关推荐

2023-04-04 12:20:28

静态资源配置

2009-09-15 16:08:00

2020-03-05 15:34:16

线程池C语言局域网

2022-06-08 09:56:46

静态链接Linux命令

2020-06-01 16:05:17

MongoDB复制集数据库

2023-03-05 16:36:14

Linux链接目标文件

2017-10-17 12:42:47

2020-10-21 10:50:24

LVSNginxHAProxy

2019-09-02 14:53:53

JVM内存布局GC

2024-01-04 08:16:34

Spring国际化标准

2019-04-09 12:16:00

2018-12-19 10:05:15

LVSNginxHAProxy

2019-07-02 06:31:27

Python虚拟环境代码

2009-06-11 16:25:44

EJB2.0EJB

2011-01-06 09:00:01

静态语言动态语言

2018-12-18 14:37:26

Spark内存管理

2009-08-28 14:09:19

C#静态类

2020-12-04 10:11:26

Unsafejava并发包

2019-05-30 11:04:52

内存Spark管理

2009-10-27 14:31:57

linux静态路由
点赞
收藏

51CTO技术栈公众号