Perl 8:次世代的脚本语言

2013-04-01 23:00

Perl 8:次世代的脚本语言

by

at 2013-04-01 15:00:00

original http://www.soimort.org/posts/152

引言:Perl的没落与重生

上个月,Parrot项目和Perl 6语言的前开发者chromatic发表了一篇题为Goodnight, Parrot的博文(reddit链接),揭露了Parrot VM项目所面临的窘境和Perl 6社区不为人所知的内幕。

Parrot项目诞生于12年前的今天(2001年4月1日),它源自于Perl之父Larry Wall和Python之父Guido van Rossum的一次头脑风暴式的短暂交谈。在那以后,一个被称作Parrot Virtual Machine的语言虚拟机项目开始了,它成为了后来的Rakudo Perl 6、也就是Perl 6的一个“官方”实现的开发基础。

Perl 6http://perl6.org/)是一次彻底改进Perl语言(的诸多落后特性)的伟大尝试,它放弃了同旧时代Perl(即Perl 5)的兼容性。遗憾的是,在提出13年后,Perl 6的开发仍然远未完成,随着Parrot项目的进展迟滞,它的正式发布目测遥遥无期。

Parrot VM最大的问题在于,它从一开始设定的目标过于野心勃勃;它不仅仅要为跑Perl 6服务,同时也要成为支持诸多动态语言的通用语言虚拟机。早在2001年的时候,Bradley M. Kuhn曾经在一篇论文中探讨过在Java VM上实现新版本Perl的可能性,指出了可能存在的问题;从那以后,Perl 6开发的方向几乎完全投向了希望渺茫的Parrot VM。然而,12年过去了,Parrot项目的完成度仍然距离最初设定的目标相去甚远。

在Perl 6和Parrot沉寂多年之后,一个关于新脚本语言的proposal被提出了;它吸引了相当一部分Perl社区的开发者:

  • 由于Parrot VM项目的不成熟和开发过于缓慢,新的语言实现应当转向更加完善与稳定的平台。而这个平台,正是曾经被Perl开发者忽略的JVM。
  • 由于旧版本Perl解释器实现的极端低效导致其饱受垢病,新语言应当更多地得益于静态类型、编译时优化,以及虚拟机的JIT(Just-in-time compilation)。
  • 新语言应当放弃同旧Perl的兼容性,正如Perl 6放弃了与Perl 5的兼容性一样;然而,新语言需要放弃得更彻底。
  • 在基于上一条,无须考虑同旧Perl兼容性的前提下,新语言应当从旧时代Perl的继任者Python、Ruby那里更加积极地吸取它们的优点。
  • 在Perl社区的关键人物Andrey Tang的提议下(正是他使用Haskell完成了第一个实验性的Perl 6实现:Pugs),新语言还应当从Haskell那里吸收尽可能多的函数式特性。

就这样,关于Perl 8.0(由于其相比旧版本Perl 5、Perl 6的巨大变化,特意跳过了7版本号)的开发被正式提上了议事日程。

它将是一门:

  • (同旧时代Perl一样)支持命令式编程的脚本语言;
  • 函数式语言,更加适合现代软件开发的并行场景(吸收了Haskell的优点);
  • 面对对象语言,“Everything is an object”(吸收了Smalltalk和Ruby的优点);
  • 强、静态类型语言,具备类型推断功能,有利于高效编译优化;
  • 以JVM为主要实现平台的语言(放弃对尚不成熟的Parrot VM实现的官方支持)。

在本文中,我将对Perl 8编程中的基础概念作一个简短的入门介绍,旨在让读者对这门全新的脚本语言有一个比较直观的认识。如果想要更深入地了解这门新兴语言,请访问文章末尾的Perl 8官网和下载链接。


Perl 8语言入门

表达式

你将注意到,Perl 8.0比起旧版本的一个重大改进,就是它引入了强大的、原生的REPL(Read-Eval-Print Loop,命令读取-求值-打印循环)支持。在Perl 8中,你将无须再借助perl -d -e 1或Devel::REPL来执行REPL;直接在命令行中输入:

$ perl8

即可进入与pythonluairb完全类似的交互式REPL模式。

Perl 8允许在REPL中进行直接表达式求值;另一个显著的改进就是,Perl语句的末尾不再需要分号。

例如,在Perl 8的REPL中,你将可以直接对简单的算术表达式进行求值:

1 + 3 * (5 - 2)
81 % 10
3.14 * 2

怎么样,是不是和你所熟悉的其他“现代”脚本语言(Python、Ruby、JavaScript……)很像?你在后面还将看到,Perl 8确实从这些新语言中吸收了许多便利的特性。

值的定义

Perl 8的另外一个重大改进,是它将由一个纯命令式的旧时代Unix脚本语言转型为一个多范式、以函数式编程为主的现代Web开发语言。这是基于现代软件中与日俱增的并行需求应运而生的。它将在高性能Web开发、分布式计算等领域大显身手,不再仅仅是一个用来对付CGI、或者用正则处理几段文本、执行些简单shell任务的胶水语言。

Perl 8中定义变量的语法比起以前更加自然。它使用新引入的var关键字:

var height = 10

正如前面所说,Perl 8是一个支持函数范式的编程语言。定义一个不变值(immutable value)需要用到val关键字:

val pi = 3.14

通过val定义的值一旦被绑定,无法在运行时被改变;使用var定义的变量则可以使用赋值语句任意改变其值,如同在任何命令式语言中常见的那样:

height += 2

在Perl 8里,用于标准终端输出的print被定义为一个函数;你必须把表达式放在一对圆括号()中:

print(h * pi)

Perl 8中使用C/C++风格的注释,支持注释块和行内注释。这使得多行注释变得更加容易:

/*
    这是一段注释。
*/
print(h * pi) // 行内注释

你同样可以发现,Perl 8从C那里吸收了更多更丰富的运算符,这使得它在保持函数式特性的同时,也同样适合硬件底层的开发。例如,我们有位操作符:

3 & 2 // 按位与
1 | 2 // 按位或
1 ^ 2 // 按位异或
1 << 2 // 左移
-24 >> 2 // 右移(保留符号)
-14 >>> 2 // 右移(零填补)

if else控制结构

在Perl 8中,if (cond) else控制结构的判断条件必须是一个Boolean表达式。

这里是一些最基本的关系运算符,由它们构成的表达式求值结果均为Boolean类型:

1 > 2 // 大于
1 < 2 // 小于
1 == 2 // 等于
1 >= 2 // 大于或等于
1 != 2 // 不等于
1 <= 2 // 小于或等于

由于Perl 8是一门函数式语言,所以,if else控制结构同样返回一个值:

if (1 > 2) 4 else 5
val try1 = if (1 == 2) 8 else 9
val isBook = 6 >= 3
val price = 16
val vol = 10
val sale = if (isBook) price * vol else price / vol

如果你想要在判断条件中连接多个Boolean表达式:逻辑算符&&||!仍然如同你所熟悉的那样,没有变化:

val isBook = 6 >= 3
val price = 16
val vol = 10
val sale = if (((isBook) && (price > 5)) || (vol > 30)) price else price / vol
sale

while控制结构

前面已经提到过,Perl 8是一个多范式语言,因此,它仍然支持命令式编程中司空见惯的循环体结构。

while (cond) block/exp控制结构允许你重复一段代码块或一个表达式,直到某个判断条件为真。

var total = 18
while (total < 17) total += 3

寻找最大公约数的算法:

// 寻找最大公约数
var x = 36
var y = 99
while (x != 0) {
    val temp = x
    x = y % x
    y = temp
}
println("gcd is", y)

for控制结构

for (range) block/exp控制结构允许你针对某个范围内的所有值,或某个迭代器的全部可能取值,重复执行一段代码。

for (i <- 1 to 4) println("hi five")

i的取值将从1遍历到4。如果不需要包含进4,可以使用until关键字:

for (i <- 1 until 4) println("hi five")

Perl 8的for循环提供了一种优雅的方式来控制多个变量的范围遍历,只需用分号分隔即可:

for (i <- 1 until 4; j <- 1 to 3) println(i, j)

实际上,for循环可以遍历任何允许迭代器访问的数据类型。例如,字符串:

for (c <- "hello") println(c)

基础数据类型

Perl 8支持的原生数据类型比以前更加丰富。它将包含如下基础数据类型:

  • Byte: 8位有符号整型
  • Short: 16位有符号整型
  • Int: 32位有符号整型
  • Long: 64位有符号整型
  • Float: 32位IEEE754单精度浮点数
  • Double: 64位IEEE754双精度浮点数
  • Char:16位Unicode字符
  • String:Unicode字符串
  • Booleantruefalse

整数

Perl 8支持四种整数类型:IntLongShortByte。你可以使用不同基数表示的字面量,包括十进制、十六进制和八进制。

十进制数:所有以非0字符打头的字面量:

17

十六进制数:以0x0X打头:

0xFF

八进制数:以0打头:

0777

在默认情况下,它们将被视作是Int类型。你可以使用lL字符作为后缀强制赋予它们Long类型:

0XFAF1L

当然,你也可以在声明值的时候显式指定其类型(当然这不是必需的,因为Perl 8支持类型推断):

val abyte: Byte = 27
val ashort: Short = 1024

浮点数

浮点数是包含小数点的字面量:

0.618

它们可以使用Ee的科学计数法表示:

val pi = 0.31416e1

在默认情况下,它们将被视作是Double类型。你可以使用fF字符作为后缀强制赋予它们Float类型。

val pi = 3.14f

字符

字符(Char)可以是任意Unicode字符,使用单引号指定:

val chr = 'A'

同样,你也可以通过八进制或Unicode表示法来表示字符:

val chr = '\101'
val chr = '\\u0041'

字符串

你大概已经猜到了,字符串仍然使用双引号指定:

val hello = "hello, world"

如我们前面提到的那样,Perl 8从Python和Ruby那里吸收了许多东西,其中就包括用来指定任意字符串的三重引号"""

print("""Welcome to Perl 8!
Click 'About' for more information.""")

布尔量

布尔量(Boolean)只有两种取值:truefalse

函数

作为一门函数式语言,Perl 8的最大亮点在于全新的函数定义方式。它吸收了Python/Ruby中的def关键字:

def max(x: Int, y: Int): Int = {
    if (x > y) x
    else y
}

对已定义的函数进行求值:

max(6, 7)

自然,递归函数定义是允许的:

def gcd(x: Long, y: Long): Long =
    if (y == 0) x else gcd(y, x % y)

作为一门函数式语言,Perl 8会对尾递归进行必要的优化。

对象

从旧时代走过来的Perl程序员也许会惊讶于Perl 8的另一项重大改进:它那对于面向对象编程的高度支持。事实上,在Perl 8里,一切均是对象(“Everything is an object”);这是它从Smalltalk和Ruby这样的语言里吸收来的另一项先进特性。

这里是如何定义一个类的例子:

class Point {
    var x = 0
    var y = 0
}

通过new关键字来创建类的实例:

val p = new Point

可以直接访问和修改类的成员:

p.x = 3
p.y = 4
print(p.x, p.y)

对于类成员变量的初始化,Perl 8提供了更加强大和灵活的语法糖:

class Point(ix: Int, iy: Int) {
    var x = ix
    var y = iy
}

val p = new Point(3, 4)

当然,你还可以定义类的成员函数:

class Point (ix: Int, iy: Int) {
    var x = ix
    var y = iy
    def vectorAdd(newpt: Point): Point = {
        new Point(x + newpt.x, y + newpt.y)
    }
}

val p1 = new Point(3, 4)
val p2 = new Point(7, 2)
val p3 = p1.vectorAdd(p2)
print(p3.x, p3.y)

Perl 8甚至还支持运算符的重载!

class Point(ix: Int, iy: Int) {
    var x = ix
    var y = iy
    def +(newpt: Point): Point = {
        new Point(x + newpt.x, y + newpt.y)
    }
    def -(newpt: Point): Point = {
        new Point(x - newpt.x, y - newpt.y)
    }
    override def toString = "Point(" + x + ", " + y + ")"
}

val p1 = new Point(3, 4)
val p2 = new Point(7, 2)
val p3 = new Point(-2, 2)
val p4 = p1 + p2 - p3
print(p4.x, p4.y)

你也许会质疑

p1 + p2 - p3

这种写法的合法性。事实上,如果写成

p1.+(p2.-(p3))

当然更加接近面向对象的思维方式(向Point对象的+-方法传递参数)。而这种省略了括号和圆点的写法,正显示了Perl 8语法糖的强大之处!

从这里你可以看到,Perl 8非常适合用来创造DSL(Domain-Specific Language,领域特定语言)

模式匹配

作为一门函数式语言,Perl 8提供了与Haskell同样强大的模式匹配语法

def decode(n: Int) {
    n match {
        case 1 => println("One")
        case 2 => println("Two")
        case 5 => println("Five")
        case _ => println("Error")
    }
}

另一种更加精简的写法是:(又一个Perl 8提供的语法糖!)

def decode(n: Int) {
    println(n match {
        case 1 => "One"
        case 2 => "Two"
        case 5 => "Five"
        case _ => "Error"
        }
    )
}


结语:Perl 8——Perl的未来!

通过以上简短的、不完全的介绍,相信你已经领略到了Perl 8作为一门多范式语言的优雅与强大之处。它摒弃了旧时代Perl 5的诸多弱点,在Perl 6失败的基础上总结了社区的经验教训,吸取了它的后辈Python和Ruby的优秀之处,师法Haskell的函数式精髓,作为一门全新的次世代语言得到了重生。

Perl 8,将会是属于未来的脚本语言。如果你对这门新兴的语言感兴趣,请访问Perl 8的官网获取更多信息(以及下载Perl 8):

最后,一个好消息是,Perl 8项目已经正式申请参加今年的Google Summer of Code 2013拉!欲知详情,请点击:http://perl8.org/gsoc2013