red,不红不专,但性感
red lang 前两天发布 0.6.3 了。osx GUI 终于得到了支持。
这事,连 hacker news 上都没掀出多少波澜。帖子在近二百多顶后,就渐渐沉底,不知去向。
我第一次听说 red 大概是 2015 年。当时在 medium 上有一篇采访 Nenad Rakocevic 的帖子,谈了 red 的思想,red 对 rebol 的传承,以及其雄心勃勃的发展计划。那篇文章里, red 1.0 预计在 1 年左右,也就是 2016 年面世,可惜现在都 2017 了,0.6.3 才姗姗来迟。
为什么 red 这样一个在座诸位可能都闻所未闻的语言会引发我的关注呢?Nenad Rakocevic 是一个原因,这哥们以后有机会再扒;另一个原因是 red 和大家耳熟能详的那些语言都不一样,非常不一样,就像吕燕盈盈立于一群貌如可儿的维密天使中间一般。
我们看看 red 妖娆到什么程度。
- homoiconic。走 lisp 系的读者应该知道这个词的逼格。用人话说就是代码即数据,数据即代码。你中有我,我中有你。
- macro / DSL。red 是 lisp 的血亲,所以 lisp 能干的那些出格的事情,red 都不在话下。
- 相当强悍的 parser(PEG)。基本上源自 rebol 著名的 parse(以后有机会扒)。
难道 red 是另外一个 clojure 或者 racket?咱们继续看。
- low memory footprint, GC & cross-compilation。这语言特性的画风突然变了,开始走 golang 路子了。是的,red 是一门编译型语言,不走虚拟机,直接编译成目标平台的,和 C 代码同级别性能的二进制(没有优化的编译结果是同样 C 代码 O2 编译 的 1/4 的性能)。编译时可以直接跨平台往 windows / linux / osx 等 target OS,以及 x86 / arm 等 target CPU 上面编译。话说 golang 的 cross-compilation 的方便程度可大不如 red。
- small target binary。还是像 golang,不过 golang 一个 hello world 好几兆,它也就几百 k。
- actors, parallel collections。concurrency 虽然不走 CSP,但用 actor 和 goroutine 也是相同水平的并发支持。
有木有一种把 lisp 塞到 golang 里的既视感?如果你对 golang 和 lisp 都没太多概念,那么我这样类比一下:就像生化危机里的 Alice 在雍正爷的家宴上化身莞娘娘跳惊鸿舞。
这还不够:
- Cross platform native GUI。好嘛,一个看上去像后端的语言竟然大刺刺去抢前端的饭碗。而且,妄图支持 osx,windows,android,iOS,一统江湖。我们都知道,但凡在 UI 层想一统江湖的主,大多都不得不走岳不群的路子,总得割掉点什么。electron 割掉的是 natvie app 的轻灵,cordova 割掉的是性能。red 和 react native 一样,割掉了些许灵活性 —— 然而它正儿八经的,在每个平台上仔细实现了 DSL 到 native code 的 compilation。[捂脸]
- rich data types (50+)。好多好多时候,我们终于不用使用那些「万能数据结构」来表达颜色,IP 地址这样的数据了(嗯,string,来,摸摸头)。
- single file toolchain。这个很逆天 - 整个编译器只有一个 binary,而且就一兆多。不科学,太不科学了。看看它能干什么事:
你告诉我一个有 UI lib,能够跨平台编译,对代码能够进行 AOT(ahead of time compile),JIT(just in time compile)和 interpret 的工具只有 1M 多?这太 TM 不科学了。
- highly expressiveness。虽然没读过文档的去写它的代码并不容易,不过在没有太多背景知识读懂(猜出)它的代码并不困难,随便举两例:
代码简单明了的不像个实力派(我知道 racket / clojure 的粉丝要不服了)。
这画风变的,仿佛一曲惊鸿舞翩翩然跳罢,Alice 又跳起了江南 style。
限于篇幅,red 的特性就先讲这么些。我们看看它的野心:
从最底层的 hardware,device driver,一路到高高在上的 application,DSL,它都想插一腿。Nenad 骄傲地将其定义为:full stack language。
full stack language?等等,你确定说的不是 javascript?[捂脸]
我们生活在一个既幸运又不幸的年代。上古时代,没那么多语言,更没那么些子分工。程序员是包打天下的黑客,匠人。如今,软件开发的领域细分到一个令人发指的地步,前后端分家,跟南北朝似的,然后前端又分裂出东西魏。前些日子 interview 时有个哥们超级自豪地自我介绍,说自己是在做 backend of backends。详问之,不禁哑然。那哥们不过是拿 node 做了些 service 的事情,就觉着自己升级成了刹帝利,急着把自己和做 API 的吠舍们撇清关系了。
精细分工的后果是程序员从黑客和艺术家蜕化成了螺丝钉 —— 端到端的包打天下越来越罕见,不再有人觉得自己了不起,觉得自己是 King of the world:写代码从一件很 cool 的,很好玩的事情变成了混饭吃的行当。后端的练好肌肉,前端的用好脂粉,便可以了,倘若后端开始修眉,前端怼人鱼线马甲线,那就乱了,是僭越。
Nenad 说不。他不觉得如今的多语种大兵团高度细分的方式是软件开发的唯一方式,或者说,最好的方式。他要发明一种语言,有足够强的表现力(好写好读),足够好的性能(避免沦为玩物),足够丰富的使用场景(前后通吃,在哪都能用),足够有趣(能够重拾编程的乐趣) —— red 是他的答案。尽管从 2012 年到现在,5 年过去了,red 还没有完全实现他的野心,但上文一一列举的特性也足够惊艳。别忘了,多数时候,他都是一个人在战斗,时至今日,整个 repo 也不过个位数的 core contributor —— github 上虽有 37 个 contributor,但绝大多数是 diff 不足百行的酱油党 。
我们看 red 的一些反潮流。
大多数语言,其运行时/工具链/库已经复杂到令人发指的地步,比如 python,几千个文件,数十个 CLI。red 就一个文件,1.2M。
大多数语言,安装和运行需要跟着文档一步步来,像作法事一样,有些二愣子,如果没有一定的目录结构和配置文件,还运行不起来。red 你只需要下载,chmod +x
,然后运行就好了。编译器,解释器,类库,cross compile 工具,全在这个 1.2M 的小小天地中。
大多数语言,编译出来的目标代码连语言他爹都不是特别清楚会长什么样,可 Nenad 竟然匪夷所思且丧心病狂地为 red 手写 arm/ia32 的 assembly / machine code(见:https://github.com/red/red/blob/master/system/targets/ARM.r),其功力可见一斑。比如这个 throw exception 时的 stack unwind:
不管你跪不跪,反正程序君是跪了。如果你对比 racket 的实现 —— racket 底层都是用 C 写的,exception 直接使用 OS 的库(比如:mach/exception.h),这就是为何都源自 lisp,red 的性能敢跟 C 看齐,而 racket 只能默默流泪。
多说两句,red 生成 ELF 可执行文件的方式也是及其反潮流 —— 人家根本不鸟 libelf 这些「庞大而垃圾」的库,也不屑走 LLVM 的正常语言都走的路,而是生生从无到有,一个 section 一个 section 攒出了 ELF —— 这哪是在编译,这妥妥的是文本处理嘛,只不过处理结果「恰巧」是个可执行的 ELF binary。
大多数语言,处理好自己的一亩三分地就可以了,但 red 志存高远。我们看图:
host interface 把从 OS API 到 hardware 的接口都考虑到了,目标是全平台 native 支持;同时 red 还不忘自己的身份,对主流语言做 bridge 增强互操作性 —— 也就是说,在 java 下你可以流着口水调用 red 的 parse,来做 rule engine。
很多人看到这可能不屑,这么强大有个卵用,还不是深宫怨妇无人知晓?的确是。red 这么多年来,几乎在大众编程语言市场,没掀起一丝波澜,有些 walking dead 的意味。不过它能却能如此坚守,已属不易。在我看来,Nenad 就像与歌利亚决斗的大卫,或者更悲壮些,是推着巨石上山的西西弗斯,尽管他所挑战的强权是那么的不可战胜,他仍然上前挑战。
就像大家耳熟能详的:
“郡主,世上不如意事十居八九,既已如此,也是勉强不来了。” “我偏要勉强。”
0.6.3 发布了,1.0 似乎还很遥远,但也不那么遥远。现在,试试就好,感兴趣可以好好把玩。希望你重新找回编程的乐趣!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- CAN总线之ISO15765协议
- Linux文本编辑命令
- UCOSii的理解和应用之消息队列
- Linux 编写Shell脚本
- Linux文件权限与归属、文件的特殊权限、文件的隐藏属性
- Linux文件访问控制列表、su命令与sudo服务
- GPS之 NMEA-0183协议
- Zeppelin 安装与初体验
- Hive 安装与配置
- 如何将SAP Cloud for Customer的扩展字段放置到Embedded Component中
- pthread的使用
- shell程序设计
- SAP CDS view权限控制实现原理介绍
- shell程序设计的流程控制
- SAP CRM数据库表CRMD_SRV_REFOBJ和CRMD_SRV_OSSET