过去5年编程语言的演化

(转载自黑客志 这里这里

前些天我编译了一个Perlis(译者注:Alan Perlis,第一届图灵奖得主)语言列表, 目的就是为了向大家展示主流编程语言之外的广阔天地,这些语言只是为了拓展你的视野,就艺术性和实现上的技巧性而言,并没有什么过人之处。在今天这篇帖子 里,我会向大家介绍一些相对较新的编程语言(让我们限定在过去5年之内),主要目的是展示这些语言中的一些有趣的特性,以及当前人们对旧有编程语言的认 识,和对下一代语言发展方向的思考和探索,下面所列的这些语言有些可能已经停止开发了,但是在某种程度上它们依然代表了人们对于“新一代编程语言”的思考 与探索,记住,这有时候并不意味着就是“创新”。

同我的Perlis列表不同,下面所列的这些语言我并没有花太多的时间去了解,所以,如果有错误还希望大家能够批评指正,另外,以下排名不分先后。

Shen

发布时间: 2011, 作者: Dr. Mark Tarver

在我的Perlis列表里,我介绍了Qi这门语言,所以在这里介绍它的继承者Shen就是理所当然的事情了,事实上,Shen和Qi的大部分(或者全部?)的特性都差不多:

* Lisp方言
* 可选静态类型
* 带保护的模式匹配
* 宏
* 交互式执行(Partial application)
* 函数回溯(backtracking)
* 内置Prolog
* 内置编译器

尽管如此,Shen实际上是Qi的进化版,Shen的很多点子都是继承自Qi,但是同Qi不同,它最主要的目标是靶向性 (targetability),这是什么意思呢?Qi主要是以Common Lisp作为它的宿主语言,并在这方面花了大量精力,尽管如此,Qi的实现实际上只使用了Common Lisp的一个非常小的子集,因此,Tarver博士决定继承这个点子,并限定Shen只依赖一个叫做KI的最小化的Lisp语言核心,因此在理论上, 这让Shen可以更容易的移植到其它宿主语言上,比如Javascript,Python,Clojure以及Common Lisp等等,“kernel Lisps”曾经激起过我的无限遐想,Shen算是在这方面的一个比较成熟的实践。

下面是一个简单的使用了嵌入Prolog的member函数:

(defprolog member
X [X | _] <--;
X [_ | Y] <-- (member X Y);)

(prolog? (member 1 [1 2 3]))
/*=> true */
交互式执行(Partial application):

(* 2)
/*=> #

((* 2) 54)
/*=> 108 */
下面这个函数用于计算第N个三角形数:

(define triangle
0 -> 0
N -> (+ N (triangle (- N 1))))

(triangle 100)
/*=> 5050 */
这是上面那个函数的带类型限定的版本:

(tc +) /* turn on type-checking */

(define triangle
{number –> number}
0 -> 0
N -> (+ N (triangle (- N 1))))

(triangle 5)
/*=> 15 : number */

(triangle a)
/* type error */
我们可以将Shen看作是Lisp漫长,蜿蜒的历史中的一个自然的进化,实际上许多现代语言只不过是从Lisp语言中吸收了其早在很多年之前就已经提出过的理念而已,因此,要说最令人激动语言,那仍然是Lisp本身。

更多资源:

* 官方网站
* 源码
* 15分钟学习Shen

Agda 2

发布日期:2009,作者: Ulf Norel

我曾试着想要全身心的投入到Agda语言中,但是关于它,我却不知从何说起,首先,Agda是一门纯函数式,模式匹配,独立类型的编程语言,并且,它还尝试实现辅助证明(proof assistants)。 在独立类型的语言中,类型表达式可以包含一个程序表达式,因此,对类型的限定就只能通过函数语句或是值预测来解决,就实在是再遭不过了,Agda的类型语 句和值语句是等价的(就是编程语句本身),这就意味着Agda的类型系统可以支持远比静态语言复杂的多的数据类型,比如,我们可以说这个函数只接受已经经 过排序的列表类型(PDF),恩,让我们看个例子吧:

下面是一个Agda类型系统的奇偶数编码程序

data Nat : Set where
zero : Nat
suc : Nat -> Nat

fortyTwo : Nat
fortyTwo = 42

plus : Nat -> Nat -> Nat
plus zero m = m
plus (suc n) m = suc (plus n m)

mutual
even : Nat -> Bool
even zero = true
even (suc n) = odd n

odd : Nat -> Bool
odd zero = false
odd (suc n) = even n
上面的代码定义了两个数据类型 1) 自然数 2)偶数,当然,你也可以定义类型函数作为前置操作符:

_+_ : Nat -> Nat -> Nat
zero + m = m
suc n + m = suc (n + m)

目前为止我对Agda的了解也就这些了,但是如果有时间,我希望可以对它做进一步的了解。

更多资源:

* The Agda Wiki
* 介绍资源
* 使用Agda的论文
* 源码

Ioke

发布日期:2008,作者:Ola Bini

Ola Bini的Ioke语言就是为了回答这样一个很简单的问题:如果完全不用考虑性能,而只专注于表达性(expressivity),那么你会创造怎样的一 门语言出来?事实证明从Bini的PPT里你可以看到这门语言具有难以置信的可表达性(见下面链接),Ioke最有趣的一个特性就是它是一个支持宏的homoiconic语言(homoiconic指程序本身可以作为语言的一种数据结构来表示)。

myfor = dsyntax(
"takes a name, an enumerable, and a transforming expr
and returns the result of transforming each entry in
expression, with the current value of the enumerable
bound to the name given as the first argument",

[argName, enumerable, argCode]

''(`enumerable map(`argName, `argCode))
)

myfor(x, 1..5, x*2)
;=> [2,4,6,8,10]

另一个促使我学习Ioke的动力就是:

牛逼的人创造的东西总是值得学习的。

不断将我们的能力推向边缘是作为程序员的职责所在,而学习聪明人的创造则是做到这点最有效的方式。

更多资源:

* Ioke
* Ioke: a Folding Language (视频)
* Ioke Wiki
* Announcement
* 源码
* Macro types in Ioke – or: What is a dmacro?

Pure

发布日起: 2008, 作者: Albert Gräf

Pure也是一个基于term rewriting构建的函数式编程语言,Term rewriting有点像是我们在高中代数里用到的FOIL(一种拆解多项式的方法)方法:

(x1 + y1) * (x2 + y2) =
(x1 * x2) + // First
(x1 * y2) + // Outer
(y1 * x2) + // Inner
(y1 * y2); // Last

上面的代码展示了如何使用FOIL方法来对两个多项式相乘进行变换,让我们来看看Pure可以做什么:

(x + 3) * (x + 5);
//=> x*x+x*5+3*x+15

或许你认为答案应该是x^2 + 8*x + 15,但是因为我们没有定义消除冗余的操作,所以结果会是下面的样子:

x*x = x^2;

(x + 3) * (x + 5);
//=> x^2+x*5+3*x+15

这已经很接近了。

另一个更复杂的例子就是求解一个数的质因子:

factor n = factor 2 n with
  factor k n = k : factor k (n div k) if n mod k == 0;
         = if n>1 then [n] else [] if k*k>n;
         = factor (k+1) n if k==2;
         = factor (k+2) n otherwise;
end;

factor 138;
//=> [2,3,23]

要说在这篇帖子里最让我佩服语言,就是属Pure了。

更多资源

* Pure wiki
* Albert Gräf访谈
* Term Rewriting and All That
* 源码

Go

发布时间: 2009,作者: Robert Griesemer, Rob Pike, and Ken Thompson

对于是否要在这篇贴子里介绍Go让我纠结了很久,到目前为止,我对Go都有非常强的抵触情绪,尽管如此,但是人们似乎觉得多一种系统级的编程语言选择是件好事情,所以最终我还是决定抛弃个人偏见,将Go包含在内。

下面是一段用Go编写的游程编码(run-length encoding)程序:

package main

import (
    "fmt"
    "strings"
)

var order = 4
var grain = "*"

func main() {
    t := []string{grain + strings.Repeat(" ", len([]int(grain)))}
    for ; order > 0; order-- {
        sp := strings.Repeat(" ", len([]int(t[0]))/2)
        top := make([]string, len(t))
        for i, s := range t {
            top[i] = sp + s + sp
            t[i] += s
        }
        t = append(top, t...)
    }
    for _, r := range t {
        fmt.Println(r)
    }
}

对于系统级编程,这个世界需要更多的选择,而Go正是针对这个目标设计的。

更多资源

* Official site
* 源码
* 指南

Arc

发布日起:2008,作者:Paul Graham以及Robert Morris

很难相信Arc居然只是一个3年前才诞生的语言(根据发布日期),当然,我之所以觉得它古老,这和我花了无数时间阅读Graham介绍Arc的帖子 有关,在Arc发布之前,它曾让人无比激动,但不幸的是,在它真正发布之后,对这个语言的评价基本上都处于失望和生气之间,我也非常想要喜欢它,但是找来 找去我还是没能找到充足的理由说服自己,我觉得如果没有Clojure,Racket或是Qi这样的语言,那么我或许会愿意去尝试下Arc,不过我必须得 承认,Arc的核心理念“简明就是力量”仍然深深的打动了我,除了语言本身(它的实现只部分遵从了这一原则),它的核心哲学依然足够强壮,让我们来看看 Paul Graham在“Arc challenge”里的这段话吧:

假设要写一个程序让某个URL(比如,http://localhost:port/said)产生一个带有输入框和提交 按钮的页面,然后当提交按钮被按下时,这时应该有第二个页面,这个页面只包含一个简单的链接“点这里”,当你点了这个链接之后,会出现第三个页面,在这个 页面你会看到“你刚刚说的是:……”,这里的省略号就是你在第一个页面所输入的内容。

要实现上面描述的功能,下面是Arc的解决方式:

(defop said req
  (aform [w/link (pr "you said: " (arg _ "foo"))
           (pr "click here")]
    (input "foo")
    (submit)))

学习Arc的一个额外的好处就是你可以顺带了解到Racket语言的神奇,好吧,我承认我作弊帮Racket做宣传了。

更多资源

* Succinctness is Power
* Arc指南
* Arc核心
* Arc论坛
* Arc源码

CoffeeScript

发布日期:2009,作者:Jeremy Ashkenas

我发现Jeremy Ashkenas绝对是个超级聪明的程序员,他的一些哲学与我不谋而合,另外值得注意的是,CoffeeScript是这篇帖子里介绍的这些语言中唯一一 个我会在实际工作中使用的语言,它的语法实在是太干净自由了,但是它最牛的地方(也有些人说这是最弱的)就是它只是对JS的非常浅的封 装,CoffeeScript实际上是对于Crockford(译者注:JavaScript社区最知名的权威之一,JSON、JSLint、JSMin 和ADSafe之父)的“Javascript: The Good Parts”一书的最好诠释,并且还是以更优美的语法实现的。

CoffeeScript最有趣的一个特性就是它提供了一种非常轻量级的JSON语法:

jsn =
  foo: 'a'
  bar: 'b'

jsn.bar
#=> 'b'

这让下面这样的对象语法变得更加容易:

sanders =
  speed: 8
  juke: 10
  report: -> "#{@speed} and #{@juke}"

sanders.report();
#=> '8 and 10'

还有列表的实现:

transpose = (matrix) ->
    (t[i] for t in matrix) for i of matrix[0]

transpose [[1,2,3],[4,5,6]]

//=> [[1,4],[2,5],[3,6]]

下面这点不知道该说好还是坏,CoffeeScript还提供了基于类支持:

class Animal
  constructor: (voice) ->
    @voice = voice

  speak: =>
    "#{@voice}!!"

class Dog extends Animal
  constructor: ->
    super("ruff")

d = new Dog
d.speak()
#=> 'ruff!!'

CoffeeScript并不是第一个基于Javascript的语言,但是它对JS的适配和支持是最完美的,并且实践证明大多数人都可以接受这种方式,而同样重要的是Coffeescript还解决了寄生语言如何与现有的JS库和框架共存的问题。

更多资源:

* 官方网站
* 源码
* CoffeeScript书籍

Potion

发布日期: 2009, 作者: _why the Lucky Stiff

曾经有个叫_why的家伙在ruby圈子里引起了一场不小的风波,作为一个神秘人,他对Ruby和编程的一些见解为他在ruby以及ruby之外的 世界赢得了很高的声誉,_why就是这样一个神奇的开源贡献者,在他的众多开源项目中,有一个叫做Potion的项目,这个项目的目标是创造一个小巧且强 大的新编程语言,Potion继承了很多Io,Lua,OCaml以及Ruby的想法,并将它们汇集于一身:

Person = class: /name, /age, /sex.
Policeman = Person class (rank): /rank = rank.
Policeman print = ():
  ("My name is ", /name, " and I'm a ", /rank, ".")
  join print.

Policeman ("Constable") print
# My name is nil and I'm a Constable.

不过让我最感兴趣的就是,Potion分离了数据和表达式的符号系统,当然我必须得承认这个语言并不是很富有创造性,这是因为作者将大部分精力都投 入到了实现上,它的实现非常简洁,只有大约6000行代码,也就是说一般公司的年轻程序员(或者是像我这样的老家伙)都可以在几周之内完全理解整个语言的 实现,阅读代码是个很好的学习方式,尤其是阅读这样出色的的代码。

继续了解

* 官方网站
* 源代码

Mirah

发布日期: 2008, 作者: Charles Nutter

JRuby算是一个非常不错的在JVM之上构建的语言了,但是就像其它许多的Java.next语言一样,它也受到运行时问题的困扰,作为一个共生 语言,要想提供一个强大且高效的开发和运行时体验,共生语言需要提供一个可以在任何java运行时上部署的核心库。如果我们将这个运行时限定在只支持一个 轻量级的运行环境,那么这在实践中就不是什么大问题。让我们来看看Mirah,Mirah的目标(这是个很靠谱的目标)就是创造一个新的基于JVM的语言 (语法主要借鉴Ruby),让你可以得到动态语言的体验,但又不需要增加任何额外的运行时库。

def reverse(s:string)
    StringBuilder.new(s).reverse
end

puts reverse('reversed')

初看起来,Mirah就是一个增加了类型声明支持的JRuby,如果我不是这样喜欢JRuby,或许我会希望Charles Nutter把他所有的时间都花在Mirah上。

继续了解

* 官方网站
* 初始声明
* 源码

Scratch

发布日期: 2007, 作者: Mitchel Resnick

当我看到我5岁的儿子毫不费力的完成了对这些乐高机器人的编程时,我就在想着,或许应该有一种提供图形化IDE的通用语言,当我发现Scratch时,我大吃了一惊,这个图形化的语言居然和我想的如此接近,并且它和上面讲的乐高机器人都是由MIT的一个小组开发的。

下面是Scratch版的“Hello,World”:

Scratch算是Smalltalk的一个近亲,但是它的目标是以“玩乐式的体验”帮助孩子学习必要的数学和编程技能——我非常赞同这个目标,Scratch虽然看起来和微软的Kodu有点像,但是基于我玩乐高的体验,我更喜欢Scratch一些。

更多资源

* 官方网站
* 源码

ClojureScript

发布日期: 2011, 作者: Rich Hickey

ClojureScript是一个基于JavaScript之上的语言,它采用了大部分Clojure的语法,然后被编译成Javascript, 因为我本人就是ClojureScript的开发者,我对它的看法难免会有偏见,所以还是让Brenton Ashworth来告诉你ClojureScript到底有多酷吧:

ClojureScript browser-connected REPL from Brenton Ashworth

这是个连我都被震住了的视频。

继续了解

* ClojureScript声明
* 介绍ClojureScript
* 源码

Clojure

发布日期: 2007, 作者: Rich Hickey

同上面一样,我对Clojure的喜爱也不可避免的带有偏见,所以我尽量少说一些,下面就是我对它的简短评价:
不变性(Immutability)将会成为游戏规则的颠覆者

另外,下面是一个由Christophe Grand实现的生命游戏的Clojure版本:

(defn neighbours [[x y]]
  (for [dx [-1 0 1] dy (if (zero? dx) [-1 1] [-1 0 1])]
    [(+ dx x) (+ dy y)]))

(defn step [cells]
  (set (for [[loc n] (frequencies (mapcat neighbours cells))
             :when (or (= n 3) (and (= n 2) (cells loc)))]
         loc)))

(def board #{[2 1] [2 2] [2 3]})

(defn print-board [board w h]
  (doseq [x (range (inc w)) y (range (inc h))]
    (if (= y 0) (print "\n"))
    (print (if (board [x y]) "[X]" " . "))))

(defn display-grids [grids w h]
  (doseq [board grids]
    (print-board board w h)
    (print "\n")))

再就是,我写过一本关于Clojure的书

更多资源

* 官方网站
* 源码

OMeta

发布日期: 2009 (?), 作者: Alessandro Warth

最近几年关于DSL的讨论越来越多,大家的很大一部分注意力都被转移到了这个话题上,尽管如此,如果有人曾经参与过DSL的设计或者实现,那么他应 该会发现,现在的情况远不能让人满意,问题就是你很难用一个通用语言实现一个既强大又健壮的DSL,造成这个问题的原因有很多,但最主要的问题就是,即使 是使用那些具有很大的语法灵活性的语言,你也仍然很难保证你实现出来的DSL不会太过丑陋,当然,你可以做到,但是如果你想要实现一个理想的DSL,你会 发现,你真的需要一套简单的工具来帮你构建一个完整的语言解析器和运行时,关于这个,我都可以花上一天再写个新帖了,但我还是先省省吧,让我们回到主题, 真的,DSL的世界需要这样一种语言,它只是为你提供一套工具,让你可以编写独立的DSL解析器,或是嵌入式的变体,没错,那就是OMeta。

Github上的OMeta示例代码

就像JetBrain的元编程系统提供的那种非常容易理解且强大的工具集一样,OMeta最大的优势就在于它非常简单,OMeta是基于语法表达式解析器(PEG)构建的,你可以轻松对它进行扩展来支持通用的数据结构。

更多资源

* 官方网站
* JavaScript源码

Fortress

Fortress有点超出了这个贴的时间限制,因为在它公开发布之前,它已经在Sun内部被使用了多年,Fortress之所以引起我的关注,只是 因为Guy Steele参与了这个项目,除了这一点,Fortress引人注目之处还在于它将精力放在了并行计算上,它的语法看起来更像是数学符号,并且支持对象- 函数范式,comprehensions,traits, contracts以及静态计量单位解析和分布式数据结构。

for i←seq(1:m) do
  for j←seq(1:n) do
    print a[i,j]
  end
end

上面的这个简单的嵌套循环展示了Fortress的一个非常有趣的特性,那就是循环中的语句是并行执行的,Fortress对于并行采用了一个很坚 决的做法,它没有将并行作为语言的一个可选项,而是将它和语言本身紧密的结合在了一起,当然,除了这种隐式的并行,Fortress也支持显式的并行计 算。

更多资源

* 项目主页
* 源码

接下来?

最近又涌现了许多新的编程语言,但是我对这些语言的了解仅限于一条Tweet的长度:

* Dart

  • 第一定理(译者注:这里指作者前面说的一句话:当牛逼的人创造了某个东西,你应该学习之)
  • 看起来为适配更多的运行时做了均衡

* Rust

  • 看起来像是D语言的继承者
  • 第一定理

* Ceylon,不确定对于一个坚持追随Java约定的团队,这个语言为他们提供了什么
* Kotlin,基于IDE打造
* Newspeak,smalltalk+: FloatD的简化版
* Perl6,鉴于我不是Perl程序员,所以我可以等
* Magpie

  • 或许是最好的关于编程语言开发的“Blog”了
  • 很不幸没时间去看它的源代码
  • 由Dart团队的一员开发
  • 感谢Tracy Harms提醒

更少人知道的

下面这些语言是其他人推荐给我的,我对它们基本上一无所知:

* Felix 属于ML系
* Wheeler 看起来真的很独特,我会花点时间探索下
* Opa 下一代的Web语言
* Frege 又一个Java.next语言,Haskell系
* Cobra 带给我合同
* Clay 另一个系统级编程语言,在我看来很干净

写完这篇文章,一个让我惊奇的发现是,过去5年间我们对于数组编程【6】 几乎没有取得什么进展,尽管如此,我怀疑可能还有有一些私有的项目没被我发现,随着时间的推移,我的感受就是,编程语言正在朝着衍生和进化的方向发展,而 不是再从头重新发明一种革命性的新语言,这并不一定就是件坏事情,因为计算机史上还有很多隐藏的很深的好点子等着我们去发掘。

我有错过什么吗?

About schemular

Just a person
此条目发表在计算机分类目录。将固定链接加入收藏夹。

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s