[转]Erlang的类型系统和静态分析

June 9th, 2009 :: jackyz

转载说明

“Erlang 是动态类型的语言,因而不能进行静态分析,所生成的文档也不包含有助于理解的类型信息”——这是惯常的看法,广为流行,而且被看作是 Erlang 在开发大型系统时的一个短板(大型系统意味着更强烈的静态分析需求和更严重的依赖文档进行沟通)。

然而 Erlang 是一个有着 20 多年历史的成熟系统,它早已发展出了一套自己的类型标注系统,不仅用来生成文档,更重要的是可以据此对源码进行静态分析,通过程序来排除一些低级的和隐藏的错误。在这方面, Erlang OTP 的源码本身及其文档就是最好的例子。在 《Erlang 程序设计》 的附录A部分,对于这个系统的使用已经进行了充分的说明。

需要强调的一点是在 Erlang 语言的背后还有一个活跃的社区(后者更为重要),其 EPP 过程一直都在持续不断地推进语言本身的进化。这方面最新的成果便是:在 R13 中,将此前文档级的 @spec,@type 标注升级为语言级的 -spec,-type 标注。可以预期的一点是,在未来的版本中,这些方面仍将持续推进。

litaocheng 同学的这篇“Erlang类型及函数声明规格”,风格严谨,论述详尽,涵盖了最新的语言特性,是任何一个程序员想要开发“严肃 Erlang 程序”的必读文档。

Read more…

study , , ,

google wave 冲击波

June 2nd, 2009 :: jackyz

警告:本文纯属六一吹水,看客童鞋小心忽悠。

认识我有一段时间的朋友,肯定都在不同的场合分别听到过我对 “实时 Web 应用” 的鼓吹。有兴趣可以翻看去年 “CN Erlounge III” 上的《Realtime Web Application》,今年“Ajax 群英会”上的《实时 Web 应用》以及“BeijingOpenParty ——晚春夜曲”上的《实时 Web 应用中间件》。

虽说俺对于实时 Web 应用的爆发早有心理准备,但在5月31日,第一眼看到 Google Wave 的时候,还是和大家一样,被震到了。一是没有想到,实时 Web 应用还可以这么玩,太 TM 有创意了;二是没有想到,在这个“还没看清怎么玩”的领域 Google 竟然这么快就现身了,太 TM 神速了。(我很好奇,是怎样的公司文化和人才机制才能孕育这么恐怖的创新能力呢?)。

好吧,惊叹就到这里了(拍 Google 的马屁,那是众 G 粉的乐子,哪轮得着我呀)。下面照例,进入咱们的 “超强脱水” 上干货环节。

创新——Google Wave 创造性的解决了“实时”与“非实时”之间的无缝连接

非实时就是 Email ,实时就是 IM ,这两者的用户体验是如此的迥异,以至于此前试图将这两者统一起来的努力均告失败。而 Google Wave 所采用的方式则是老调重弹却又谱出新章的 Version Control —— 一个 Wave 就象是 Wiki 上的一篇文章,是一个有着版本控制的信息实体,被多个 participants (也就是用户)所共享。在这个隐喻下,非实时的访问就变成了“获取当前版本”,而实时的访问则符合“跟踪当前版本的所有更新”(所有的更新都通过实时消息广播给所有的 participants)。至此,让人目瞪口呆的 Replay 功能,也变得极其简单,只不过是版本控制的酷眩呈现而已。

现在看到很多说法,从“将会一统天下”到“未来互联网的基础应用”,等等,溢美之词不一而足。姑且认为这些都是 G 粉丝美好的 YY 吧。无须过渡诠释,咱们抛开 Google 的明星气质和强大的号召力不谈(借助这些, Google Wave 真的有成为 THE ONE 潜质也说不定)。单从“请下神坛”的“低俗化”角度, Google Wave 最本质的创新其实就是 Realtime + Wiki 并在此基础上进行各种扩充而已。有心山寨的各位山大王,从这里开始抄就对了。

技术——基于 Web 的 Realtime 技术,以及有 Google 特色的开发框架

在创新面前,任何实现层面的技术都会显得黯然失色。虽然目前还无缘得见 Google Wave 的 Realtime 实现技术,但大致可以猜出肯定是 Web Socket 或者 Ajax Long Poll 中的一种(很有可能是前者)。从技术层面而言,早已经算不上是新鲜事物了。然而有 Google 特色的开发框架(这个句式怎么听来这么耳熟呢),仍让人感到新奇。展现层的扩展直接使用了 Gadgets 模式。逻辑层的扩展则直接使用了 App Engine 的 Web 模型。

应该说,这一整套扩展方案充满了 “Google 的味道”,而且,这个模型肯定是能用的。除了 Google 系扩展一贯的稍嫌“重型”之外,唯一让人感到担心的,恐怕是这种逻辑扩展模型的抗压性。虽说使用了批量发送作为两个层次之间的阻抗匹配,但这种方法实际上仍然是将实时性更高的 Realtime 通讯构建在了实时性较低的 HTTP 通讯之上,不知道这会不会带来潜在的问题。

战略——Browser Platform/OpenSocial/OpenStand阳谋的最新尝试

Google 对 M$ 的阳谋就是 Browser Platform 战略,浏览器即平台——管你什么设备,也不关心什么 OS ,只要有一个符合 HTML5 标准的浏览器,走到哪里都是一样的用。无论是 gOS 也好,Chrome 也罢,还有 Google 对 Firefox 的大力支持,所有东西全都围绕这个核心展开。利益攸关嘛,所以,在 HTML5 上 Google 的心急和 Microsoft 的拖沓,也就显得很好理解。而这次的 Google Wave 也没有例外。以超眩的用户体验为“ HTML5 兼容浏览器”阵营奉上自己的 Killer App。民心向背是革命成败的关键,Google 的革命理论学得不错。

M$ 之外,另外一些会让 Google 这样的巨头感到巨头疼的恐怕也就只有 Facebook/Twitter 这一票跟在屁股后面猛追不舍的 SNS 了。OpenSocial 战略就是分化瓦解 SNS 集团的阳谋,但似乎不大奏效。第二梯队的 SNS 始终各怀鬼胎,最起码的 Open Platform 尚且无法贯彻,更进一步的 OpenSocial 缺乏动力也就见怪不怪了(从国内 SNS 对“开放平台”的暧昧态度就可窥见一斑)。因为无法摆平各方利益,技术相当领先的 OpenSocial 几乎快到了要沦为鸡肋的境地。而第一梯队的 Facebook 之辈用户基数则继续保持爆炸性增长,搞得 Google 很是窝心的说。如果说之前的 OpenSocial 是“跟在后面搞破坏”,那么此番祭出的 Wave 则是“抢在前面打埋伏”,而且还摆明了车马,将要彻底开源,欢迎大家山寨。用心可谓良苦,但效果目前还说不好。用户到底是会被 Network 粘得死死的,还是会被 Wave 勾得心痒痒进而纷至沓来,仍然还需要时间的检验。但,计以至此却已然到了尽头,再没有回转的余地;如若不售,后续的手段恐怕只剩下收购一条了。

OpenStand 对于 Google 来说,乃是性命之根本。试想,如果某个“封闭标准”的公司独占互联网哪怕是十分之一的内容,而且不开放内容给 Google 的爬虫(考虑一个充血版的 Facebook),那么搜索结果的质量会因此受到多大的影响?在这个普通网民都开始谈论“实时搜索”的时代,在底层(也就是通讯协议层)占据一个有利的位置(推出自己的开放标准,或者保证标准的开放性),对于 Google 这样的公司来说是一件多么重要的事?从这个角度来看,被事先高调宣布的 Google Wave 的 Open Source Product 和 Open Protocol 路线图,无论怎么看都是必须的一步棋。

Google Wave 不是实时 Web 应用的银弹

其实这是一句屁话——哪怕是在某个犄角旮旯的小不点儿领域,这个世界上也都从来就没有过什么银弹。我得承认,说“Google Wave 不是实时 Web 应用的银弹”,完完全全就是为了寻开心。一个软件老民工装出一副思想很深邃的样子,恶心一把装腔作势的软件砖家们,没办法,就是这么低级趣味。

首先,我也承认 Google Wave 眩得要命,客户方的代表看了也会喜欢得不得了,但它完完全全是基于 HTML5 标准的——你的意思是想去说服公司领导,让他们完全抛弃占据市场份额超过 80% 的广大 IE 浏览器用户么?XD,脑子笨一点不要紧,关键是不要进水就好了。

其次,我知道 Google Wave 提供了 Robots API 很多东西都可以“引入”到系统中,而利用 Gadgets API 又可以“展现”很多东西,况且 Google Wave 本身还会开源,哎呀,简直太好了,我们可以在 Google Wave 上架构我们的系统,既省钱来又省时间,这下终于有时间去泡妞了……。喂,醒一醒,XD。我们真的需要把我们的应用嵌入(接入) Google Wave 以获得 Realtime 特性,而不是相反?尤其是,当我们的应用并不像个 SNS 或者“协作系统”的时候也要如此?好吧,算你牛,非得这样,那,咱们项目组要不先回家待岗,等它出来了咱们再回来上班?

再还有,协作隐喻能够覆盖所有的 Realtime App 需求么?这是一个根本性问题,要不,咱再想想……?

干货上完,现在上水货。地址在[这里],各路水神水仙纷纷出动,精彩得紧。不要问我这水到底有多深,你自己下去探探不就知道了。;)

misc

他山之玉,以及其他

May 26th, 2009 :: jackyz

他山之玉

活跃于 .NET 技术圈的老赵同学,最近写了一个不错的文章系列——《一个轻量级Actor模型实现》。关于 ActorModel 可以参考《 Erlang Programming 》第七章 “并发” (98页)中的形象描述,也可以参照 Wikipedia 上 ActorModel 词条的严谨定义。

简而言之,所谓的 ActorModel 其本身的概念是极简单的,它只有两个基本的要素:每个 Actor 顺序地处理消息,多个 Actor 并发地执行。应该说,它是结构复杂(尤其是并行)应用程序的一种合理抽象。Joe Armstrong 在他的《 Erlang Programming 》第七章 “并发” 中认为:ActorModel 是从并发角度对人类社会的交互和协作行为进行模拟的结果。而在实际运用中,这一抽象模型,也早已广泛运用,其中的一些,我们还相当熟悉,比如:*nix 的进程和管道系统、 Email 系统、所有的 MQ 中间件,等等。作为抽象模型的 ActorModel ,它并不局限于某种特定的语言。换言之,采用任何一种语言(或者技术)都可以实现自己的 ActorModel 。

老赵以 .NET 实现的 ActorModel 原型,在基本特性上,已经具备了 ActorModel 的要素,细节可能仍有优化的余地。作为他自己独立思考和深入思辨的产物,不仅是对 .NET 技术领域的同学具有学习价值,对于 Erlangor 而言,因为他的模型是以 Erlang 作为样本的,而他实现过程的思辨,也给我们提供了相当独特的角度,反过来,对于我们理解 Erlang 本身的特性(为什么 Erlang 会被设计成这个样子),也很有帮助。

如下几个问题,建议 Erlangor 们在阅读老赵的文章时,顺便想想:

  • Erlang 的轻量级线程与 ActorModel 的任务调度有何关系。
  • Erlang 的动态类型+模式匹配方案和 ActorModel 的消息处理有何关系。
  • ActorModel 和 Singleton 以及 Lock-Free 又有何关系。

感谢 xushiwei 的 twitter 投递。

其他

以下是 blah 部分,含水量极高,请部分读者自行无视。

国内的 Erlang 应用进入“低调务实”阶段?

  • sohu 最新推出的 SNS —— “白社会”,使用 MochiWeb 作为 update notify 的 server 。打开 firebug 的 net 面板,很容易就能发现这一点(有请 sohu “白社会”的同学出来介绍一下详细情况)。
  • litao.cheng 同学在某公司也采用了 MochiWeb + erlang … 来实现某统计业务。
  • yufeng 同学在某公司采用 Erlang 实现动态集群。
  • xushiwei 同学在某公司采用 C 来实现 Erlang 的某些特性,据说效果不俗。
  • ……

再搞 Erlounge CN 的话,肯定会大有可观。

推荐阅读《Unix 编程艺术》

这一本书说起来是久闻大名,我却中了网络评论的毒,以为它是“拜 Unix 教”的《转X轮》或者是 Linux 老愤青 YY 的《中国不高兴》,所以一直也都没想过要找来看看。

某日,终于还是在图书馆(图书馆好地方啊)的一个书架前相遇了。随手抽出来翻了一下,本来准备扫一眼就放回去的,没想到就此读了一个下午,几乎到了让人放不下来的地步。相见恨晚啊。

尤其是书的第六章,作为一个 Erlangor 来说,共鸣尤为强烈。之前,总听人说 Erlang 的内部构造很象是一个 Unix 系的操作系统,这话听得多了,却总没什么感性认识,直到看了这本书,才觉得说得千真万确。

这种相似性,不只是体现在部分模块的命名/设计和很多思想的一脉相承上,更体现在 KISS 和以数据为核心的价值取向上。Erlang 的整个运行系统就好象是一个为并发进行过增强之后了的 Unix 系统(尤其是进程子系统和进程间通讯子系统),运行在它之上的程序就好比是 shell/perl 的脚本,以简单而强大的方式,通过由若干个彼此通讯的进程组成的计算网络来完成任务。

从这个角度,说 Erlang 是 Unix 核心思想朝并发方向的延伸和进化,并非只是 Erlangor 一厢情愿地想“往 Erlang 脸上贴金”的说法。

顺便查了一下这本书在 douban 上的评价,吃惊的发现进行过推荐的牛人严重扎堆。鄙视一下自己的火星。

关于 Concurrent Programming in Erlang Part2

知名的《Concurrent Programming in Erlang》一直都只在 Erlang.org 开放了 Part1 供大家免费下载,它的 Part2 因而充满了种种神秘的色彩。为了揭开这个谜底,我上次特地跑到“国家图书馆”外文阅览室,费了牛鼻子劲,终于将这本很可能是全中国唯一的一本,弄将出来瞧上了一瞧。下面是观感:

原来这本书的 Part2 内容比较杂,每个部分的篇幅内容也比较少,还略显有点过时(肯定的,是1996年出的,已经13年过去了)。尤其是其中讲 Database 的部分,似乎彼时连 Mnesia 都还没有成形,还有讲 GUI 的部分,使用也很少。其中的大部分内容,在网上都能找到其他的论述文章。可能唯有第 12 章 Operating Systems 的内容(也就是上述“Erlang OS 很象 Unix”的观点)似乎还没有看到相应的资料(知道的同学请补充之)。好在这部分内容关注的是 Erlang 的内部构造,它的缺失想必也不会太多的影响到大家的使用,所以关系应该不大。

情况就是这样子,貌似各位不需要惦记了。

misc

Erlang Factory 2009 Videos

May 20th, 2009 :: jackyz

Erlang Factory 大概算得上是 Erlang 世界在瑞典之外规模最大的会议了。之前在美国加州举办的 Palo Alto 为期两天,日程满满,好话题多到让人眼晕。这次 Erlang Factory 2009 – Palo Alto 放出了一堆好视频,地址在[这里](谁知道 PPT/PDF 在哪里可以看到?请跟帖分享之)。英国 London 在 6月22日到6月24日还会有另外一场欧洲 Base 的 Erlang Factory 不知道社区里面会不会有人参与其中呢?

misc

couchdb style offline storage?

May 9th, 2009 :: jackyz

Mozilla Labs 的 Atul Varma 发表了一篇博文[看这里],探讨使用一个浏览器内置的类似 CouchDB 的系统[看这里]来作为浏览器离线存储方案的可能。——于无声处闻惊雷!我得说,这真是一个天才的点子。

提起目前的 SqLite 方案,说实话,这的确是一个由 “直接神经传导” 产生的 “拿来主义” 方案(简而言之,就是说它很直观啦),而且确实可用。但,恐怕也很难有人能够再想出一个比 “在浏览器中使用 SQL ” 更让人昏昏欲睡的主意来了。就这一条,原本看起来还 “飘出未来的气味” 的浏览器开发,一个激灵,就能让人感觉出逼人的 “幽幽古意” 来。

本该如此,浏览器从来都是 JavaScript 的天下。一切设施都围绕 JavaScript 而存在,存储也不应该例外。这里的 CouchDB 其实和 Erlang 无关,它只是一个 “风格供应商” 而已。顺带的,如果在统一 Online/Offline 存储接口(这是 W3C 的问题) 之后要提供存储/同步服务(这是 Google 之类的问题) ,只有到了这个时候,才没准会有 CouchDB 的事(如果没有更好的替代的话)。但,创意真的是一个具有美感的东西。不是么?

唯一的疑惑是,我也知道 CouchDB 很久了,为什么就没有想到这个用法呢?

感谢 “力” 的投递,感谢 “zly06” 的推荐。

misc

[转] couchdb 上手指南

May 6th, 2009 :: jackyz

张沈鹏同学写了一篇不错的 couchdb 上手指南,以 wiki 格式发在 erlang-china 的 maillist 里。这里整理了一下格式,原文转贴出来,并郑重推荐之。

启动

balin couchdb #  ./utils/run

参数有

-h display a short help message and exit
-V display version information and exit
-a FILE add configuration FILE to chain
-A DIR add configuration DIR to chain
-n reset configuration file chain (including system default)
-c print configuration file chain and exit
-i use the interactive Erlang shell
-b spawn as a background process(作为后台进程)
-p FILE set the background PID FILE (overrides system default)
-r SECONDS respawn background process after SECONDS (defaults to no respawn)
-o FILE redirect background stdout to FILE (defaults to $STDOUT_FILE)
-e FILE redirect background stderr to FILE (defaults to $STDERR_FILE)
-s display the status of the background process
-k kill the background process, will respawn if needed
-d shutdown the background process(关闭)

配置

balin couchdb # vi etc/couchdb/local_dev.ini

可以指定端口号等,常用的有:

[httpd]
port = 12345
bind_address = 0.0.0.0

[admins]
用户名 = 密码

使用

http://123.123.123.123:12345/_utils/

可以创建数据库

python 中的使用

http://123.123.123.123:12345/_utils/database.html?python-tests

以操纵这个数据库作为演示,python库有几个函数比如update([...])不能用,不能用用户名密码等等,也许要修一下…

from couchdb import client
from couchdb.client import Document
server = client.Server('http://123.123.123.123:12345/')
 
#打开数据库
db = server['python-tests']
 
#创建一条数据
doc_id = db.create({'type': 'Person', 'name': 'John Doe'})
 
#获取一条数据,这个doc接口和字典一样
doc = db[doc_id]
 
#_rev是版本,_id是uuid
doc.items()
[(u'_rev', u'1-2963977070'),
(u'_id', u'4a36f238f4facbe08762b1a958cef39e'),
(u'type', u'Person'),
(u'name', u'John Doe')]
 
#可以自己指定主键
db['JohnDoe'] = {'type': 'person', 'name': 'John Doe'}
 
db['JohnDoe'].items()
[(u'_rev', u'1-2744716443'),
(u'_id', u'JohnDoe'),
(u'type', u'person'),
(u'name', u'John Doe')]
 
#更新
badman = db['JohnDoe']
badman[age]=1234
db['JohnDoe'] = badman
 
#删除,可以用db.delete(doc)来删除
del db['JohnDoe']
 
#遍历
for row in db.view('_all_docs'):
  
print row.id
 
#看数据库信息
db.info()
{
u'compact_running': False,
u'db_name': u'python-tests',
u'disk_size': 24381,
u'doc_count': 13,
u'doc_del_count': 0,
u'instance_start_time': u'1241518867280531',
u'purge_seq': 0,
u'update_seq': 21}
 
#文档可以有2进制的附件 put_attachment 用这个函数上传
# 查询,map_fun是一个js函数,emit是emit(key,value)。key,value均可是null
# web页面上有Select view查询,可以直接搜索测试
# 好像要用unicode字符 不然找不到 囧啊
 
db['/logo/xxx1.jpg']={"type":"logo","size":1}
db['/logo/xxx2.jpg']={"type":"logo","size":2}
db['/logo/xxx3.jpg']={"type":"logo","size":3}
db['/logo/xxx4.jpg']={"type":"logo","size":4}

Map函数

map_fun = u'''
function(doc) {
   if (doc.type=='logo')
       emit(doc._id, doc.size);
}
'''
 
for row in db.query(map_fun):
  
print row

输出

<Row id=u'logo/xxx1.jpg', key=u'logo/xxx1.jpg', value=1>
<Row id=u'logo/xxx2.jpg', key=u'logo/xxx2.jpg', value=2>
<Row id=u'logo/xxx3.jpg', key=u'logo/xxx3.jpg', value=3>
<Row id=u'logo/xxx4.jpg', key=u'logo/xxx4.jpg', value=4>

我们还可以加上reduce函数,比如:

  1. reduce_fun = u'''
  2. function(keys, values, rereduce) {
  3.    return sum(values)
  4. }
  5. '''
  6. for row in db.query(map_fun,reduce_fun):
  7.    print row

输出

<Row key=None, value=10>

reduce 中 rereduce变量的含义如下

  1. rereduce为false
    • key为array,element为:[key,id],key为map function产生的key,id为Document对应id
    • values为array,elements为map function产生的结果
    • 比如 reduce([ [key1,id1], [key2,id2], [key3,id3] ],[value1,value2,value3], false)
  2. rereduce为true
    • key为null
    • values为array,element为前一次reduce返回的结果
    • 比如reduce(null, [中间结果1,中间结果2,中间结果3], true)

[这里]有一些map/reduce演示的例子,比较好懂.

Creating Views
View 可以理解为索引了 不过这个索引不是实时的…
接着上文的例子

db["_design/test"]={
 
"views":
 
{
  
"all": {
    
"map": "function(doc) { if (doc.type == 'logo')  emit(null, doc) }"
  
},
  
"size_large_than_2": {
    
"map": "function(doc) { if (doc.size && parseInt(doc.size)>2)
emit(null,doc) }
"
  
},
  
"total_size": {
    
"map": "function(doc) { emit(null,parseInt(doc.size)) }",
    
"reduce": "function(keys,values) { return sum(values) }"
  
}
 
}
}

然后刷新

http://123.123.123.123:12345/_utils/database.html?python-tests

可以看到 select views中多了test,也可访问:

http://123.123.123.123:12345/python-tests/_design/test/_view/all

可以加上limit这一类参数

http://123.123.123.123:12345/python-tests/_design/test/_view/all?limit=2
http://123.123.123.123:12345/python-tests/_design/test/_view/all?limit=2&skip=1

这样可以分页,不过[这里]提到

A simpler method of doing this is to use the skip parameter to work out the starting document for the page, however this method should be used with caution. The skip \parameter simply causes the internal engine to not return entries that it is iterating over. While this gives the desired behaviour it is much slower than finding the first document for the page by key. The more documents that are skipped, the slower the request will be.

所以最好配合下面的startkey之类的来用skip
类似参数还有:

  • 排序 descending=false
  • 开始结束 startkey=”abc”&endkey=”abcZZZZZZZZZ” 可以用 docid startkey_docid=null
  • 合并结果 group=true 用法有点复杂,可以看[这里]

key可以是复杂的key,比如:

The query startkey=["foo"]&endkey=["foo",{}] will match most array keys with “foo” in the first element, such as ["foo","bar"] and ["foo",["bar","baz"]]. However it will not match ["foo",{"an":"object"}]

python中可以这样访问

for row in db.view('_design/test/_view/all'):
  
print row.id

输出

logo/xxx1.jpg
logo/xxx2.jpg
logo/xxx3.jpg
logo/xxx4.jpg

又如

for row in db.view('_design/test/_view/size_large_than_2'):
  
print row

输出

<Row id=u'logo/xxx3.jpg', key=None, value={u'_rev': u'1-3347158087',
u'_id': u'logo/xxx3.jpg', u'type': u'logo', u'size': 3}>
<Row id=u'logo/xxx4.jpg', key=None, value={u'_rev': u'1-1107796651',
u'_id': u'logo/xxx4.jpg', u'type': u'logo', u'size': 4}>

网络资源

  1. 根据网上的测试表明:couchdb 写入速度 比 mysql 慢4倍,创建索引速度 比 mysql 慢50倍
  2. couchdb 只写入不删除,需要定期做整理,类似垃圾回收的copy+删除,需要预留大量磁盘空间.
  3. 索引不是实时的,你可能看到的是旧的数据

我的个人看法
单单看性能,couchdb的确很不理想,但是couchdb可以把数据以view的方式展现,要什么,就新建什么样的view,这种随心所欲索引方式,在不少应用的场合,通过view的方式把这种查询结果持久化,可以大大减少了把传统意义上的重复且相似查询.

study

[荐]talk to erlang in ruby with Erlix

April 29th, 2009 :: jackyz

KDr2(Killy Draw)同学做了一个 Ruby – Erlang 的项目 Erlix 。象这种富有新意(Talk to Erlang as A Node 而不是 as A Port, Like erlectricity)的民族软件(KDr2 Speaks Chinese),而且文档也比较完整,实在是找不出不大力进行推荐的理由。

项目主页:http://code.google.com/p/erlix/ || http://github.com/KDr2/erlix/

代码下载:http://erlix.googlecode.com/files/erlix-0.2.zip

使用教程:http://code.google.com/p/erlix/wiki/ErlixTutorial

另外,还有买一赠一惊喜大特惠!这里再推荐一篇通过 Erlang Node 机制进行通讯的原理性文章(刚巧也是 Ruby 的),如果想要实现一个自己的 Talk to Erlang as A Node in xxx Language (貌似好几个语言都已经有了自己的版本,大家动手之前,先 google 一下,看看别人做了没有),相信这一篇 [ 点这里 ] 也会很有参考价值。

news

[荐]Erlang的Unicode支持

April 10th, 2009 :: jackyz

勤奋的 litaocheng 同学,在每日超负荷的加班工作之余,仍然刻苦学习笔耕不辍,为我们不断带来劲爆文章,这一篇《Erlang的Unicode支持》为我们介绍了 R13 的最新特性,也是最被大家期望的特性——内置的 Unicode 支持。废话少说,直接上正文。

在R13A中, Erlang加入了对Unicode的支持。本文涉及到的数据类型包括:list, binary, 涉及到的模块包括stdlib/unicode, stdlib/io, kernel/file。

Binary

Binary的type属性增加了utf相关的type:utf8, utf16, utf32,其分别对应UTF8, UTF16,UTF32编码。

Binary Constructing

在Binary构建时, 如果指定了utf相关类型,那么对应的integer的Value必须位于:0..16#D7FF, 16#E000..16#FFFD, 或者 16#10000..16#10FFFF这三个区间中。否则将会提示’bad argument’,参数错误。根据指定的的utf类型不同,同一个数据产生的binary不同。

对于utf8,每个integer生成1到4个字符;对于utf16,每个integer生成2或4个字符;对于utf32,每个integer生成4个字符。

比如, 使用unicode为1024的字符A, 构建一个binary:

1> <<1024/utf8>>.   
<<208,128>>
2> <<1024/utf16>>.
<<4,0>>
3> <<1024/utf32>>.
<<0,0,4,0>>

Binary Match

当进行Binary Match时,如果指定utf相关类型,变量成功匹配后,将拥有一个位于:0..16#D7FF, 16#E000..16#FFFD, 或者 16#10000..16#10FFFF这三个区间中的integer。

其更具utf类型的不同,消耗(match)不同数目的bytes。

  • utf8匹配1-4个bytes(参考RFC-2279)
  • utf16匹配2 或 4 个bytes (参考 RFC-2781)
  • utf32匹配4个 bytes

比如:继续我们上面的例子

4> Bin = <<1024/utf8>>.
<<208,128>>
5> <<U/utf8>> = Bin.
<<208,128>>
6> U.
1024

这个例子中,U匹配了2个bytes。对于utf相关类型,不能指定unit spec。

List

在list中,每个unicode字符采用integer来表示,因此与latin1的list相比,unicode list中,element的数值可以大于255。下面就是一个有效的unicode list: [1024, 1025]

我们可以通过unicode 模块实现 list到binary的转换。

unicode module

首先请参看下面的type定义:

unicode_binary() = binary() with characters encoded in UTF-8 coding standard
unicode_char() = integer() representing valid unicode codepoint
chardata() = charlist() | unicode_binary()
charlist() = [unicode_char() | unicode_binary() | charlist()]
a unicode_binary is allowed as the tail of the list

external_unicode_binary() = binary() with characters coded in a user specified Unicode encoding other than UTF-8 (UTF-16 or UTF-32)
external_chardata() = external_charlist() | external_unicode_binary()
external_charlist() = [unicode_char() | external_unicode_binary() | external_charlist()]
an external_unicode_binary is allowed as the tail of the list

latin1_binary() = binary() with characters coded in iso-latin-1
latin1_char() = integer() representing valid latin1 character (0-255)
latin1_chardata() = latin1_charlist() | latin1_binary()
latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()]
a latin1_binary is allowed as the tail of the list

我们可以调用unicode:characters_to_list/1 将chardata或latin1_chardata或external_chardata()转化成一个unicode list。

如果参数为latin1_chardata,那么Data参数就是一个iodata. 返回的结果list中,每个element为一个integer。默认情况 unicode:characters_to_list/1调用unicode:characters_to_list(Data, unicode)

如果我们的CharData为其他类型,我们可以指明InEncoding type。如果此函数执行成功,返回{ok, List},如果失败返回{error, list(), RestData}, 其中list为转化成功的部分,RestData为发生错误的位置。

我们也可以调用unicode:characters_to_binary/1,将chardata或latin1_chardata或 external_chardata()转化成一个binary。这个函数和unicode:characters_to_list类似,只是结果保存为 binary。

如果Data为latin1_chardata, 那么unicode:characters_to_binary/1和 erlang:iolist_to_binary/1功能相同

unicode模块中,还有两个于bom相关的函数,可以根据bom指返回对应的encoding类型,也可以根据encoding类型生成对应的bom值。其在保存文件时,经常使用.

Examples

1, 打开utf8保存的文件

文件内容如下test.file:

[
desc, "这是一个测试文件"},
{
author, "litaocheng"}
].

其格式为erlang term,保存时选择utf8编码。
代码如下:

  1. %% read content from the file
  2. test1() ->
  3.     {ok, [Terms]} = file:consult("test.txt"),
  4.     Desc = proplists:get_value(desc, Terms),
  5.     _Author = proplists:get_value(author, Terms),
  6.    
  7.     % out put the Desc and Author
  8.     DescUniBin = iolist_to_binary(Desc),
  9.     DescUniList = unicode:characters_to_list(DescUniBin),
  10.     io:format("desc bin : ~ts~ndesc bin : ~p~n",[DescUniBin, DescUniBin]),
  11.     io:format("desc list: ~ts~ndesc list: ~p~n", [DescUniList, DescUniList]).

结果:

desc bin : 这是一个测试文件
desc bin : <<232,191,153,230,152,175,228,184,128,228,184,170,230,181,139,232,
             175,149,230,150,135,228,187,182>>
desc list: 这是一个测试文件
desc list: [36825,26159,19968,20010,27979,35797,25991,20214]

首先将内容从list转换为binary, DescUniBin 便是对应的unicode binary。随后通过unicode:characters_to_list/1转化为unicode list最后输出。
我们可以看到 unicode list中所有的element为integer, unicode binary中unicode string采用uft8编码。

2, 将数据保存成uft8格式

  1. %% save the binary in utf8 format
  2. test2() ->
  3.     [DescList] = io_lib:format("~ts", ["这是一个测试文件"]),
  4.     DescBin = erlang:iolist_to_binary(DescList),
  5.     DescList2 = unicode:characters_to_list(DescBin),
  6.     List = lists:concat(["[{desc,\"", DescList2, "\"}, {author, \"litaocheng\"}]."]),
  7.     Bin = unicode:characters_to_binary(List),
  8.     io:format("bin is:~ts~n", [Bin]),
  9.     file:write_file("test_out.txt", Bin).

这个例子的完整代码如下:

  1. -module(unicode_test).
  2. -compile([export_all]).
  3.  
  4. %%
  5. %% the test.txt content:
  6. %% [
  7. %% {desc, "这是一个测试文件"},
  8. %% {author, "litaocheng"}
  9. %% ].
  10. %%
  11.  
  12. test() ->
  13.     test2(),
  14.     test1().
  15.  
  16. %% read content from the file
  17. test1() ->
  18.     {ok, [Term]} = file:consult("test_out.txt"),
  19.     Desc = proplists:get_value(desc, Term),
  20.     _Author = proplists:get_value(author, Term),
  21.    
  22.     % out put the Desc and Author
  23.     DescUniBin = iolist_to_binary(Desc),
  24.     DescUniList = unicode:characters_to_list(DescUniBin),
  25.     io:format("desc bin : ~ts~ndesc bin : ~p~n",[DescUniBin, DescUniBin]),
  26.     io:format("desc list: ~ts~ndesc list: ~p~n", [DescUniList, DescUniList]).
  27.  
  28.  
  29. %% save the binary in utf8 format
  30. test2() ->
  31.     [DescList] = io_lib:format("~ts", ["这是一个测试文件"]),
  32.     DescBin = erlang:iolist_to_binary(DescList),
  33.     DescList2 = unicode:characters_to_list(DescBin),
  34.     List = lists:concat(["[{desc,\"", DescList2, "\"}, {author, \"litaocheng\"}]."]),
  35.     Bin = unicode:characters_to_binary(List),
  36.     io:format("bin is:~ts~n", [Bin]),
  37.     file:write_file("test_out.txt", Bin).

misc

[荐]平行處理概觀

April 7th, 2009 :: jackyz

这篇发表于 ZDNet tw 站上的《Programming for Parallelism》,是 Intel 赞助的内容,虽说有广告嫌疑,而且通篇都是读起来让人亲切之余还有些吃力的 “正体中文” (幸好初中读过一本繁体竖排版的《封神榜》打了点基础),而且满眼尽是晕乎乎的台式技术术语(幸好大学读过一本繁体横排版的《组合语言程式设计》,注: “组合语言” == “汇编” ,又打了点基础)。但是内容确实没得说,相当之有料!清晰简明,三两句就把原来觉得神秘兮兮的东西讲明白了——保持了海峡对岸兄弟们技术文章的一贯高水准。我是一口气读完了,大呼过瘾,如果你也能够耐着性子读完,肯定会很有收获。

“并行编程 (Parallel Programming)” 和 “并发编程 (Concurrent Programming)” 是一码事么?如果不是,那它们之间到底有什么区别呢?

这个问题,在看完这篇文章之前,我还真回答不上来。因为,确实不了解,我也知道 OpenMP、MPI 这些被当作是并行编程的 “必杀密技” 必须要去了解一下,但我一听见这些强烈 C 风格的东西就私底下直犯触,老是没法鼓起勇气(和兴趣)去研究它们。这下好了,这篇文章以极其精炼的语言和高度抽象的视角(主要是这个,老实说,我对细节还真的不是很感兴趣),把这些 “难点” 一下子都讲明白了。

以我不求甚解的标准来看,“并行编程 (Parallel Programming)” 在抽象方法上的:优先考虑并行、大量制造任务、限制锁的使用。以及基础设施中的:基本的可并行算法和数据结构、互斥、计时、任务计划、MPI 的消息传递机制,等等。其实和强调 “并发编程 (Concurrent Programming)” 的 Erlang 在概念上基本 “略同” 。所不同的是,这些设施大多都是为 C 系语言准备的,号召广大 C 程序员向并发 “靠拢” ,而 Erlang 则是彻底决裂,自己搞了一个语言出来(把这些具体的实现细节隐藏在虚拟机里面)。

又长知识了,感谢方块兄的投递,感谢 INET6 的推荐。

misc

关于 Erlang in JVM 的讨论

April 4th, 2009 :: jackyz

貌似最近 Erlang4J 又被很多人炒(吵)起来。似乎隔不了多久,社区中的一部分,就要被这类话题给搅和一番。为此 Hypothetical LabsKevin 同学发了一篇火力十足的猛文,相当精彩,也没有被墙,就不转载了,请到[这里]欣赏。

通常,这种火力十足的帖子都会引发围观,在 Erlang 的邮件列表,这种事情也已经发生过好几起了,实在不足为奇。这里罗嗦的一点观战提示是:跟帖质量相当之高,同样值得一读。

misc