显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

为着理想勇敢前进

 
 
 
 
 
 

老诗新译

2014-4-19 0:02:11 阅读25 评论0 192014/04 Apr19

我把一首脍炙人口的古诗翻译成了英文。

Future Song

The Future flatMaps a Future.
The Future tailcalls forever.
My life to await the Future.
It comes OutOfMemoryError.

能猜到是哪首诗吗?

答案在这里:https://github.com/Atry/stateless-future#future-song

作者  | 2014-4-19 0:02:11 | 阅读(25) |评论(0) | 阅读全文>>

Justin Donaldson博士点评几大Haxe异步框架

2013-2-24 2:39:11 阅读458 评论0 242013/02 Feb24

译者按:Justin Donaldson博士是牙买加卫生部的技术顾问,BigML公司的创始人。他最近发布了一个开源库Promhx,通常用来控制node.js等异步流程。

接着就有人问:您的大作和haxe-continuationhx-async相比如何?(注:haxe-continuation是鄙人拙作,在Haxe+Node.js+continuation打造高性能高开发效率服务器架构中介绍过)

Justin Donaldson博士做出了以下答复:


(译文)

我前不久刚看过这两个库,haxe-continuation的英姿深深地映在了我的脑海中。

我用hx-async时举步维艰。这玩意儿各种API和语法都不少,闪瞎了我的狗眼也没搞明白。要我说,hx-async给人的第一印象就是没教养。

haxe-continuation似乎是用非阻塞方式假冒Future……这“奇技淫巧”倒真是颇为优雅。haxe-continuation对宏的用法算得上一部严谨的教科书了。你敢信吗?它竟用宏完全改写了整个函数,这才让你产生阻塞调用的幻觉。

它在许多细节上都处理得不错,比如它支持从异步函数中返回多个返回值。你要是接收返回值的变量名不够,它居然还会给出正确的编译错误信息:

https://github.com/Atry/haxe-continuation/blob/master/tests/TestNode.hx

它创建多个线程的方式也颇为精彩。一句简单的Lambda.iter().async(),就创建了5个线程,之后的代码像单线程一样正常编写即可。这招简直绝顶聪明,代码可读性比典型的CPS风格代码强太多了。

唯一的缺点?大量使用宏重排了整个函数。建议心理承受能力不足的人别去读它生成的JavaScript代码。这种代码调试时不好跟踪,幸亏浏览器的SourceMap功能弥补了这个缺陷。此外……@cps函数的返回值必须是Void类型,因为这些函数通常只被@cps代码调用。不过,这些做法完全无可厚非。

Promise是一种包装异步返回数据的方式,尤其用于收集多个异步调用后返回同一执行流的情况。这算是和haxe-continuation相比的主要优点吧。但即便如此,依我看,有了haxe-continuation,再也不需要去实现什么Promise或Deferred了……这东西真真有趣。


(原文:https://plus.google.com/112651821425677274147/posts/PuTmizEhEr4

作者  | 2013-2-24 2:39:11 | 阅读(458) |评论(0) | 阅读全文>>

Dotsies是加速英文阅读的神器吗?

2012-12-16 1:59:24 阅读563 评论2 162012/12 Dec16

最近我花了不少时间来学习Dotisies。Dotsies是用点构成的拉丁字母。比如这一句用Dotsies写的文字:

Dotsies是加速英文阅读的神器吗? - 杨博 - 为着理想勇敢前进

它其实相当于英文的dotsies is here

以下是Dotsies的字母表,和拉丁字母一一对应:

Dotsies是加速英文阅读的神器吗? - 杨博 - 为着理想勇敢前进

官网说Dotisies节省横向空间,学熟了以后认单词都是认整个轮廓的,所以读得很爽。听起来很有道理吧,我想以后用它编程省空间啊,一个屏幕可以竖排好几个编辑窗口,还可以提高我阅读代码的速度。所以我就决定去学Dotsies了!


学了一会发现不对劲,咋这单词就这么难认呢?从我使用的感受而言,dotsies甚至比英文的识别性更差。

  1. 英文单词由语素构成,比如unbreakable其实是un-break-able三个语素。dotsies把字母连成一饼,肉眼很难分割单词内部的语素了。
  2. 英文有很多长元音组合,比如eaayaioaowuiueey。在dotsies中这些长元音组合都是断开的两截,很难作为整体识别出来。请看下图:
    Dotsies是加速英文阅读的神器吗? - 杨博 - 为着理想勇敢前进
    这是以Doties表示的英文:“leaf”。其中ea是一个长元音组合。人的视觉皮层更容易根据拓扑连接关系来识别,所以识别这个单词时会割裂成左边、左下和右上三部分,然而发音时,却需要把中间两个字母看成一个整体,这和拓扑关系不符,所以读起来可费劲了。

要是把字母连一起可以更容易从整体识别的话,那你干嘛不这样用:

Dotsies是加速英文阅读的神器吗? - 杨博 - 为着理想勇敢前进

估计没人会觉得靠上的一行“The quick brown fox jumps over the lazy dog”更易读吧?

说到底,字母连一起,真的不好认!

作者  | 2012-12-16 1:59:24 | 阅读(563) |评论(2) | 阅读全文>>

那么,尼古拉斯的Haxe支持自动CPS转换吗?

答案是,不提供原生支持。

还记得上篇中提到的抽象语法树宏吗?有了宏,我们根本不需要Haxe原生支持CPS转换,我们可以自己为Haxe添加语言特性。haxe-continuation正是一个基于宏实现的CPS转换

安装

至此,Haxe+Node.js+continuation都已经介绍完毕。最后,我将介绍这三大技术开发环境搭建的完整过程:

  1. 下载并安装Node.js:http://nodejs.org/download/
  2. 下载并安装Haxe:http://haxe.org/download
  3. 打开控制台(Windows 7和Vista需要以管理员身份打开控制台)
  4. 初始化haxelib。在控制台输入
    haxelib setup
  5. 安装hx-node。在控制台中输入
    haxelib install nodejs
  6. 安装hx-continuation。在控制台中输入
    haxelib install continuation

现在你已经拥有了“现在在地球上开发复杂逻辑的服务器时,能达到顶级异步IO性能,同时只需要低水平程序员就能理解和维护的技术架构

编译

我在中篇中贴出了一段“要是能这样写就好了”的代码。其实那段代码,真的可以编译并运行。按以下步骤运行成功后,你会看到这段程序创建了一个TestNode文件夹,然后在里面创建了五个文件。

  1. 创建TestNode.hx,内容如下:
    import js.Node;
    import com.dongxiguo.continuation.Continuation;
    using Lambda;

    @:build(com.dongxiguo.continuation.Continuation.cpsByMeta("cps"))
    class TestNode
    {
    /**
    * Writes <code>content</code> to <code>fd</code>.
    */
    @cps static function writeAll(fd:Int, content:String):Null<NodeErr>
    {
    var totalWritten = 0;
    while (totalWritten < content.length)
    {
    var err, written =
    Node.fs.write(
    fd, content,
    totalWritten, content.length - totalWritten, null).async();
    if (err != null)
    {
    return err;
    }
    totalWritten += written;
    }
    }

    /**
    * Creates a directory named "TestNode", and concurrently put 5 files into it.
    */
    @cps static function startTest():Void
    {
    var err = Node.fs.mkdir("TestNode").async();
    if (err != null)
    {
    trace("Node.fs.mkdir failed: " + err);
    return;
    }

    // Lambda.iter() forks threads for each element.
    // Fork 5 threads now!
    var fileName = ["1.txt", "2.log", "3.txt", "4.ini", "5.conf"].iter().async();

    // Note that some asynchronous functions return more than one values!
    // It's OK in CPS functions, just like Lua.
    var err, fd = Node.fs.open("TestNode/" + fileName, "w+").async();
    if (err != null)
    {
    trace("Node.fs.open failed: " + err);
    return;
    }

    // Invoke another CPS function.
    var err = writeAll(fd, "Content of " + fileName).async();
    if (err != null)
    {
    trace("Node.fs.write failed: " + err);
    return;
    }

    var err = Node.fs.close(fd).async();
    if (err != null)
    {
    trace("Node.fs.close failed: " + err);
    return;
    }
    }

    public static function main():Void
    {
    startTest(
    function():Void
    {
    trace("Test is done!");
    });
    }
    }
  2. 把TestNode.hx编译成TestNode.js。在控制台中输入
    haxe --dead-code-elimination -lib continuation -lib nodejs -main TestNode -js TestNode.js
  3. 运行TestNode.js。在控制台中输入
    node TestNode.js

现在打开TestNode.js,你会发现这个文件和中篇开头列出的源码一样有着十层缩进的Callback Hell。

唯一的区别是,你永远不再需要手写这些恐怖的代码了。

(完)


相关资源:

作者  | 2012-11-24 18:16:19 | 阅读(1161) |评论(2) | 阅读全文>>

我在Haxe+Node.js+continuation打造高性能高开发效率服务器架构(上)中谈过了JavaScript的第一个死穴——功能弱。今天谈谈第二个死穴——Callback Hell

Callback Hell

假如你想用Node.js创建一个文件夹,然后在这个文件夹中建立五个文件,大概你需要这样写:

writeAll = function(fd,content,callback) {
var totalWritten = 0;
var continue5 = null;
continue5 = function() {
if(totalWritten < content.length) {
fs.write(fd,content,totalWritten,content.length - totalWritten,null,function(err,written) {
var endIf7 = function() {
totalWritten += written;
continue5();
};
if(err != null) callback(err); else endIf7();
});
} else callback();
};
continue5();
}
startTest = function(callback) {
fs.mkdir("TestNode",null,function(err) {
var endIf0 = function() {
["1.txt","2.log","3.txt","4.ini","5.conf"].forEach(function(fileName) {
fs.open("TestNode/" + fileName,"w+",null,function(err1,fd) {
var endIf1 = function() {
writeAll(fd,"Content of " + fileName,function(err2) {
var endIf2 = function() {
fs.close(fd,function(err3) {
var endIf3 = function() {
callback();
};
if(err3 != null) {
console.log("Node.fs.close failed: " + err3);
callback();
} else endIf3();
});
};
if(err2 != null) {
console.log("Node.fs.write failed: " + err2);
callback();
} else endIf2();
});
};
if(err1 != null) {
console.log("Node.fs.open failed: " + err1);
callback();
} else endIf1();
});
});
};
if(err != null) {
console.log("Node.fs.mkdir failed: " + err);
callback();
} else endIf0();
});
}

尽管为了减少嵌套,我把写入数据的代码放到了单独的函数中,但嵌套层次仍然达到了10层。如果用四个空格缩进,80字符换行,那么光是缩进就占去了屏幕一半的宽度 。

错误的解决办法

callbackhell.com想出的解决办法是给函数命名、拆成平坦的函数和以及模块化。给函数命名当然不会错,然而平坦化和模块化用来解决几十行代码内的超深嵌套则是高射炮打蚊子,其实会让问题更糟。

这是因为平坦化和模块化破坏了函数的局部性,让读者难以理解。我们通常谈及CPU局部性,是指CPU访问相邻内存时要比访问远处内存时更快,因为相邻的内存有更大的可能性位于高速缓存中。人脑和CPU一样,工作记忆很小,思考时如果思路不连贯效率就低下。

平坦函数

平坦函数让本应连续的代码在编辑器中距离更远。原本代码的读者只需要在原地看看下一行就知道程序流程,而拆成平坦函数后,读者则需要去寻找引用的回调函数。尤其在JavaScript这种动态类型语言中,读者见到一个回调函数名时,甚至不知道这个变量名到底是字符串还是整数,很难领会到这是个回调函数,更难找到这个函数的定义之处。

模块化

模块化也不是解决嵌套问题的好办法。如果有人因为嵌套层次太深看不清同一个文件中的几十行代码,那如果几十行代码分到多个文件,他一定更难搞懂了。原本的问题只是肉眼对齐缩进的难度问题,分成多个文件后却需要他去理解模块间的引用关系。恕我直言,模块化只是把复杂度从单个命名函数转移到了模块之间而已,并不能降低理解代码的难度。《Unix编程艺术》建议的模块最佳尺寸是数百行代码啊,模块化绝不能用来解决几十行代码内部的问题。

自动CPS转换

其实Node.js的异步回调风格API有一个术语,叫做CPS(Continuation-passing style)。比如函数式编程语言Haskell的所有IO相关API其实都和Node.js一样,也靠传递回调函数方式运行。

比如,如果在Haskell中要输出三行文字,你可以这样写:

main = (putStr "What is your name?")
>> ( (putStr "How old are you?")
>> (putStr "Nice day!")
)

瞧这嵌套的层次,是不是觉得和咱们蛋疼的Node.js差不多了?

根本不是!人家Haskell有do语法糖,上述代码可以用语法糖写成这样:

main = do putStr "What is your name?"
putStr "How old are you?"
putStr "Nice day!"

这语法糖的写法看着又和同步的API差不多了吧?这在Haskell中叫Monad,该语法糖与自动CPS转换等效。

这就是解决Callback Hell的正解。

简单的说,我们需要以同步IO的语法写程序,然后想个办法自动转换成异步回调的CPS风格代码。比如,同样是用Node.js创建一个文件夹,然后在这个文件夹中建立五个文件,要是能这样写就好了:

/**
* Writes <code>content</code> to <code>fd</code>.
*/
function writeAll(fd, content)
{
var totalWritten = 0;
while (totalWritten < content.length)
{
var err, written =
fs.write(
fd, content,
totalWritten, content.length - totalWritten, null).async();
if (err != null)
{
return err;
}
totalWritten += written;
}
}

/**
* Creates a directory named "TestNode", and concurrently put 5 files into it.
*/
static function startTest()
{
var err = fs.mkdir("TestNode").async();
if (err != null)
{
alert("fs.mkdir failed: " + err);
return;
}

// Lambda.iter() forks threads for each element.
// Fork 5 threads now!
var fileName = ["1.txt", "2.log", "3.txt", "4.ini", "5.conf"].iter().async();

// Note that some asynchronous functions return more than one values!
// It's OK in CPS functions, just like Lua.
var err, fd = fs.open("TestNode/" + fileName, "w+").async();
if (err != null)
{
alert("fs.open failed: " + err);
return;
}

// Invoke another CPS function.
var err = writeAll(fd, "Content of " + fileName).async();
if (err != null)
{
alert("fs.write failed: " + err);
return;
}

var err = fs.close(fd).async();
if (err != null)
{
alert("fs.close failed: " + err);
return;
}
}

现在,唯一的问题是,我们怎么才能用上自动CPS转换功能呢?

要知道我们的主题是“只需要低水平程序员就能理解和维护的技术架构”,如果改用Haskell显然太难了。

尼古拉斯的故事

另一门支持自动CPS转换的语言是OCaml。

有一个法国人叫尼古拉斯,他很喜欢OCaml,但他也意识到OCaml、Haskell这类学术口味太重的语言难推广。于是尼古拉斯用OCaml发明了一门大众口味的编程语言。这门大众口味的编程语言语法很像JavaScript,支持宏,支持带类型推断的静态类型系统,可以编译到7种语言和无数种平台(包括Node.js)。

这门语言叫Haxe.

(未完待续)

作者  | 2012-11-13 0:14:42 | 阅读(1150) |评论(0) | 阅读全文>>

目前存在许多服务器框架,它们的上手难度有高有低,性能也有高有低。但是,现在在地球上开发复杂逻辑的应用服务器时,能达到顶级异步IO性能,同时只需要低水平程序员就能理解和维护的技术架构的,只有Haxe+Node.js+continuation

Node.js的优势和死穴

最近Node.js很流行。Node.js的优势是:

然而Node.js有两大死穴:

  • JavaScript太弱。虽然JavaScript上手容易,但难以驾驭大型应用程序。
  • Callback Hell。Node.js的代码缩进常常达到数十层之多,让正常人类无法维护。

JavaScript有什么问题?

最近几年出现了许多可以编译成JavaScript的新语言,包括谷歌公司的Dart、微软公司的TypeScript以及Motion Twin合作社Haxe。这三门语言都强调自己语法和JavaScript类似,上手容易。可是,为什么人们不愿意直接编写JavaScript而要发明新的语言呢?这些语言试图解决什么问题?

到这些语言的官方网站找出他们的共同之处:

TypeScript
TypeScript offers classes, modules, and interfaces to help you build robust components.
These features are available at development time for high-confidence application development, but are compiled into simple JavaScript.http://www.typescriptlang.org/
Dart
Dart is a class-based, object-oriented language with lexical scoping, closures, and optional static typing.http://www.dartlang.org/
Haxe
HaXe offers very tight type checking. Having compile-time checks in place allows you to catch errors in your code BEFORE testing it in the browser. This allows for a faster workflow for dynamically typed targets such as PHP and JavaScript.http://haxe.org/doc/why

它们和JavaScript最明显的区别就是,它们具备了基于类和接口的静态类型系统。这一区别也正是JavaScript和Java、C++等“大型”应用编程语言相比所缺少的东西。王·不是博士·垠说:“给参数和返回值加上类型标记,不管是对人还是对编译器都有好处,也不费很多工夫。”可JavaScript偏偏就不支持,搞得很难用一种统一的方式描述API原型,大量的低级错误也无法在编译时检查出来。

什么语言能成为JavaScript的后继者?

尽管TypeScript、Dart还是Haxe都支持面向对象的静态类型系统,但我要推荐的只有Haxe。

Haxe的优势是:成熟度、杀手级框架和杀手级特性。

成熟度

先比比最无趣的成熟度吧。

  • Haxe的最新版本号是2.10,而Dart和TypeScript都还没发布1.0版。
  • Haxe在Github上排行48,Dart排行54,TypeScript未上榜。
  • Dart和TypeScript支持的功能,Haxe几乎都支持
  • IDE方面,Haxe的IDE是FlashDevelop,它具备完整的代码提示和调试功能,而且是我用过的所有IDE中最不卡的。

杀手级框架

我给杀手级框架的定义是:此时此刻,面对某个特定问题问题,在地球上唯一能找到的成熟解决方案。

虽然PureMVC这种垃圾框架也有Haxe版,但我所知道的Haxe真正杀手级框架只有两套:nme和(node+continuation)。nme的牛逼之处我不打算讲给竞争对手听,而(node+continuation)正是本文将要介绍的内容。

杀手级特性

Haxe的杀手级特性是,用来在编译时产生抽象语法树。这种宏属于语义宏,和Lisp的宏类似,而不是C语言臭名昭著的字符替换宏

Haxe宏的用处是扩展Haxe语法本身,只有在语言原本机制不够用时才有必要。通常应由主程序员编写宏为框架定制语法,大多数程序员只需要遵守这些语法不需要自己编写宏。比如Haxe的continuation必须用宏实现,但使用continuation的一般程序员并不需要了解宏的细节就能轻易使用和维护。

(未完待续)

作者  | 2012-10-27 18:26:42 | 阅读(1521) |评论(7) | 阅读全文>>

今天去工商局办事,别人送我的桌游

2012-8-10 21:54:52 阅读357 评论2 102012/08 Aug10

这两天办公司注册,跑工商局好累。最后他们奖给我一副连连看的桌面游戏:
连连看的桌面游戏! - 杨博 - 为着理想勇敢前进

动画版:
连连看的桌面游戏! - 杨博 - 为着理想勇敢前进

好玩极了

作者  | 2012-8-10 21:54:52 | 阅读(357) |评论(2) | 阅读全文>>

只要能修好我的硬盘,我甘愿成为一名好人

2012-8-4 1:50:34 阅读280 评论2 42012/08 Aug4

老外有个软件叫ISO to USB,号称可以把光盘映像放到U盘或移动硬盘上启动。

我把移动硬盘分了两个区,我打算把一张启动盘做到其中一个新分区上。于是我就高兴的打开了ISO to USB,然后选择了新的那个分区。然后……

我的移动硬盘两个区上的所有东西就都没了……

在这万念俱灰的时刻,我想起了一句古老的咒语:

只要能修好我的硬盘,我甘愿成为一名好人 - 杨博 - 为着理想勇敢前进
好人修电脑,坏人床上搞

只要能修好我的硬盘,恢复我的数据,我甘愿成为一名好人。我在好人卡前宣誓:

我决心遵照女神的领导,不再勾搭,不再约炮,准备着,为好人主义事业,贡献出一切力量!

——时刻准备着!

作者  | 2012-8-4 1:50:34 | 阅读(280) |评论(2) | 阅读全文>>

Debian squeeze的PPTP VPN客户端配置翻墙

2012-8-1 11:47:02 阅读447 评论0 12012/08 Aug1

apt-get install ppp pptp-linux resolvconf

必须安装resolvconf不然DNS会出问题。

因为我们是为了翻墙,所以要修改/etc/resolvconf/interface-order,把VPN的DNS优先级设置得高一些。这样做可以避免DNS污染。

vim /etc/resolvconf/interface-order
# interface-order(5)
lo.inet*
lo.dnsmasq
lo.pdnsd
lo.!(pdns|pdns-recursor)
lo
tun*
tap*
hso*

# 注意,这里把ppp*放到了eth*上面
ppp*

eth*
ath*
wlan*
*
然后要创建/etc/ppp/peers/your_vpn_name/etc/ppp/chap-secrets,详见 http://pptpclient.sourceforge.net/howto-debian.phtml#configure_by_hand

最后修改/etc/network/interfaces,增加以下配置:

auto ppp0
iface ppp0 inet ppp
provider your_vpn_name
metric 0

作者  | 2012-8-1 11:47:02 | 阅读(447) |评论(0) | 阅读全文>>

怎样玩《跑跑龟》才能欺负人

2012-5-13 12:34:03 阅读277 评论0 132012/05 May13

怎样玩《跑跑龟》才能欺负人 - 杨博 - 为着理想勇敢前进

《跑跑龟》是个很简单的桌游。基本上接触过桌游的人都玩过吧。看上去《跑跑龟》没啥策略性,输赢也就看运气而已。这么想你就错了,跑跑龟其实是个浅度策略游戏,如果你按以下方式玩,胜率可以达到80%。(注:本文是按“同时到终点时上面的乌龟赢”的规则来写的)

  • 中前期没用的牌可以随便出,但有用的牌要留着。所有的牌按有用程度排序如下:
    1. 任意颜色乌龟前进两格
    2. 自己颜色乌龟前进两格
    3. 任意颜色乌龟前进一格
    4. 自己颜色乌龟前进一格
    5. 任意颜色乌龟后退一格
    6. 特定颜色乌龟后退一格
    7. 最后一名乌龟前进两格
    8. 其他颜色乌龟前进两格
    9. 最后一名乌龟前进一格
    10. 其他颜色乌龟前进一格
  • 怎样玩《跑跑龟》才能欺负人 - 杨博 - 为着理想勇敢前进自己颜色乌龟前进两格的牌很宝贵,前期一般不会用。如果前期有人用了前进两格的牌,那么他多半不是这种颜色的乌龟。
  • 在走完赛程一半之前,尽量要让自己的乌龟落后
  • 过了赛程一半之后,要尽力让自己乌龟踩住别的乌龟而不要被别的乌龟踩住。如果前期你单独落后,那么只要你前进就能踩住别人。如果你被别的乌龟踩住,你就让自己头顶的乌龟前进或后退而把自己露出来。
  • 如果对方乌龟移到了只剩最后两步的位置,一定要用自己的乌龟压上去。最好不要单独压上去,而是让自己脚下的乌龟驼着自己压上去。如果没有办法把自己的乌龟压上去,就用后退牌让对方乌龟退回来。
  • 轮到你行动时,如果只剩两格了而自己的乌龟在顶上,那么用你先前憋的好牌冲线吧。

作者  | 2012-5-13 12:34:03 | 阅读(277) |评论(0) | 阅读全文>>

简单还是复杂?

2012-4-25 20:16:54 阅读436 评论0 252012/04 Apr25

作者:Martin Odersky

译者:杨博

原文:Simple or Complicated ? 

最近我们在激烈讨论,有人说Scala对正常程序员来说很复杂,有人说Scala其实是一门编码很简单的语言。有两个帖子在讨论(http://michid.wordpress.com/http://warpedjavaguy.wordpress.com/2010/08/02/the-scala-is-too-complex-conspiracy-1/)。后面的回帖也值得一看。 

看了这些帖子和回帖后,我觉得很多人没讲到一块,完全是鸡同鸭讲嘛。所以我想举例说明这个问题不像表面上看的这么简单。 

 

简单还是复杂? - 杨博 - 为着理想勇敢前进                 简单还是复杂? - 杨博 - 为着理想勇敢前进

哪边更简单?

造哪个更简单?显然是前者。 

用起来更简单的呢?取决于……哼哼哼。如果你没用过键盘,也没用过电话,你可能觉得学莫尔斯电码还容易点。但我们其他人肯定觉得智能手机更简单啊。 

和别的语言比,Scala更像智能手机而非莫尔斯电报机。Scala编译期和核心标准库本身是极其复杂的软件,但用户体验却自由又友好。我有证据证明Scala真心易用。瞧瞧基于Scala的Kojo(http://netbeans.dzone.com/learn-scala-with-kojo),是教小屁孩初学编程用的。再说印度有上百名高中女生从Scala开始学编程(http://www.himjyotischool.org/)。这就是证据啊,即使不是名校博士生或者超级牛人之类的普通人也能上手嘛。

你可以说这样比不公平。智能手机和莫尔斯电报机都是硬梆梆的硬件,编程语言却很开放,更像可以拼起来的玩具套装而非消费品。 

好,那我们就看看可以拼起来的玩具套装。  

简单还是复杂? - 杨博 - 为着理想勇敢前进           简单还是复杂? - 杨博 - 为着理想勇敢前进

哪边更简单? 

左边的摩比世界的形状种类要比右边的乐高多,所以你可能觉得乐高简单些。 

另一方面,对搭积木这件事来说,摩比世界提供的是固定的方案,而乐高则提供了近乎无限的选择。某些人有选择恐惧症(患病的不一定是他们本人,也可以是他们的同事)。对他们来说,摩比世界比乐高简单。 

Scala更像乐高而非摩比世界。从客观指标看,Scala的概念并不多,还算简单。比如咱们可以比比各种语言的关键字数量(http://carlosqt.blogspot.com/2010/07/how-many-keywords-do-you-type-in-your.html)。我自己从头比了比上下文无关语法的数量,Scala相当紧凑,和Haskell或OCaml相当,比Java5稍少,比C#和C++少得多。 

(Scala作为一种语言,的确比图灵机或Lambda演算复杂,但后两者就相当于程序语言中的莫尔斯电码。Scala也比Python、Scheme或者Clojure复杂,但Scala有完备的静态类型系统,不可避免的增大了语言的开销)。 

从另一角度看,因为Scala的许多概念都很通用很正交,所以能以各种方式组合起来。所以毫无疑问Scala为完成几乎所有任务都提供了多种方式供选。这就要求通过实践找出比其他方式更好的最佳方案。因而也要求你必须接受,有时达到好的设计会存在不止一条途径。 

你对这些比较可能仍然怀有不满,因为不管怎么说,几乎所有的编程语言都图灵完备,所以在这个层面上,每种语言都能实现其他所有语言的功能。而摩比世界可并不完备,至少摩比世界造不出寿司大餐。 

那么我们用乐高和乐高比,要得不? 

简单还是复杂? - 杨博 - 为着理想勇敢前进            简单还是复杂? - 杨博 - 为着理想勇敢前进

哪边更简单?


左边的乐高得宝系列显然比右边的乐高科技系列简单,没错吧? 
没错,但如果给你个任务让你造一个比较特别的东西,你用哪个系列?可能用得宝系列你也能造出来,但一定比用科技系列别扭。更有可能你需要用到积木得宝系列中根本没有(比如绳状或胶状积木)。 

现在你该猜中了 :-) Scala更像科技系列而非得宝系列。相比别的语言,Scala能引导我们更简洁的解决问题。Scala还可以进行依赖注入(http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di.html),而别的语言则需要引入外部配置文件才能做到。 
显然,这意味着Scala每行代码干的活更多,所以你会提出Scala比那些代码冗长的语言更难理解。但有实验证明(http://infoscience.epfl.ch/record/138586)事实并非如此,最终程序员会更适应紧凑的代码而非冗长的代码。 

我希望我能让你相信,“简单还是复杂”的辩论本身就不简单。复杂度是个多维的矢量。在一场讨论中,水友们笔下的“复杂度”往往是指复杂度不同维度上的分量。


作者  | 2012-4-25 20:16:54 | 阅读(436) |评论(0) | 阅读全文>>

尼玛Java真是太灵活了

2012-3-3 23:54:16 阅读457 评论2 32012/03 Mar3

我想知道Java程序打日志有多少种方式配置,于是画了一个图:

尼玛Java真是太灵活了 - 杨博

尼玛Java真是太灵活了!!!!!!!

作者  | 2012-3-3 23:54:16 | 阅读(457) |评论(2) | 阅读全文>>

大量new临时对象可以是一种优化手段

2012-2-29 8:24:23 阅读811 评论5 292012/02 Feb29

有C++经验的人往往知道,频繁在堆上分配对象对性能伤害很大。比如这样的代码就不是好代码:

void foo(int[] a) {
WrappedArray* wrappedArray = new WrappedArray(a);
wrappedArray->bar();
delete wrappedArray;
}

然而在JVM(指HotSpot虚拟机)上完全不是这样的。尽管JVM不提供显式在栈上创建对象的功能,但JVM会自动通过逃逸分析找出生命周期在函数范围内的临时对象,然后运行时优化这个函数,把对象创建放到栈上。逃逸分析在JRE7里面已经默认开启了,对临时对象多的代码效果很大。

Scala的隐式类型转换特性很鼓励创建临时对象。标准库中就有大量的隐式转换,会生成许多临时对象,如WrappedArrayStringAddWithFilter等。我读标准库的源码时甚至感到他们已经把创建临时对象当成一种优化手段了。

比如C++常见的pimpl模式往往会让用户代码持有一个包装对象的指针,包装对象的内部有一个底层实现对象的指针。这种做法在Scala里面就完全不被主张,而更鼓励长期持有底层对象,而在需要用时再临时包装成对外接口。

Scala的Array对应Java中的数组,功能很少,但Array可以隐式转换为功能全面的ArrayOps,使用ArrayOps上的函数时会生成多个临时对象。试看如下代码:

val myArray = Array("foo", "bar", "baz")
for (element <- myArray if element.contains('a')) { println(element) }

Scala编译器会把上述代码转换成这样:

val myArray: Array[String] = Array.apply("foo", "bar", "baz")
new (new ArrayOps[String](myArray)).WithFilter(
new AbstractFunction1[String, Boolean] {
override def apply(element: String): Boolean = {
return new StringOps(element).contains('a')
}
}).foreach(new AbstractFunction1[String, Unit] {
override def apply(element: String) {
println(element)
}
})

颤抖吧骚年,这一小段代码中居然有五处new,创建了ArrayOps[String]ArrayOps[String]#WithFilterAbstractFunction1[String, Boolean]StringOpsAbstractFunction1[String, Unit]整整五个临时对象。

Scala宁愿创建这么多临时对象也不用pimp模式是因为pimpl模式比直接持有底层对象多保存一个包装对象。如果在容器中保存大量包装对象时,额外的内存开销就会多起来,而pimp模式还会导致每次函数调用也多一次间接寻址的开销。另一方面,只持有底层对象的话,虽然原则上每次使用都会生成临时对象,但是这些临时对象经过JVM的逃逸分析、标量替换和内联优化后是零开销的。

但临时对象的开销取决于JVM的优化,然而JVM要在运行时优化,而且只有频繁运行的代码才会被优化。所以如果一个程序创建很多临时对象,那么刚启动时它的性能会很差,而运行一段时间后会慢慢变好。这也是为什么我们经常会感觉Java程序启动慢的原因之一。

作者  | 2012-2-29 8:24:23 | 阅读(811) |评论(5) | 阅读全文>>

印第安人质疑中国小学课文

2012-2-5 13:35:56 阅读539 评论2 52012/02 Feb5

印第安人质疑中国小学课文 - 杨博 - 为着理想勇敢前进

我小学时有一篇课文说讲了个故事,大意是

  1. 人的头盖骨很坚硬
  2. 生命生长的力量更大,只要把种子塞到头盖骨里面让种子发芽,头盖骨就被撑开了

我觉得这是胡扯嘛,同样是生命生长的力量,女人裹小脚时怎么没听说过撑破裹脚布的?

再说,按课文的意思,头盖骨如此坚硬,生命生长的力量如此大,那么可以推论,头盖骨生长的力量应该是世界上最大的力量了吧。于是印第安人就用自己的头做实验,用木板夹一夹,看看世界上最大的力量能否顶穿木板。

后来他们发现了真相:顶不穿的……

为了纪念他们为科学献身的精神,他们被称为扁头族(Flathead)

作者  | 2012-2-5 13:35:56 | 阅读(539) |评论(2) | 阅读全文>>

狮身人面像的逆袭,圆周率=5.1961524227...

2012-1-30 22:39:42 阅读726 评论2 302012/01 Jan30

最近有反华势力在造谣说π=4。笑死人了,圆周率怎么可能是整数?圆周率分明是“三倍根号三”好不好?
真相:圆周率=5.1961524227... - 杨博 - 为着理想勇敢前进
我是圆周率
只有掌握了正确的圆周率5.1961524227,才能建造出宏伟的狮身人面像和金字塔。

作者  | 2012-1-30 22:39:42 | 阅读(726) |评论(2) | 阅读全文>>

查看所有日志>>

 
 
 
 
 

License

 
 
模块内容加载中...
 
 
 
 
 
 
 
 

广东省 深圳市 双鱼座

 发消息  写留言

 
博客等级加载中...
今日访问加载中...
总访问量加载中...
最后登录加载中...
 
 
 
 
 
 
 
日志评论
评论列表加载中...
 
 
 
 
 
 我要留言
 
 
 
留言列表加载中...
 
 
 
 
 

八方来客

 
 
模块内容加载中...
 
 
 
 
 
 
 
模块内容加载中...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2014

创建博客 登录  
 加关注