4. Lisp cmdline库
接下来,小伙伴们发现了Lisp方言Racket的cmdline库。
02 | (parse-command-line "compile" (current-command-line-arguments) |
05 | ,(lambda (flag) (verbose-mode |
06 | ( "Compile with verbose messages" )] |
08 | ,(lambda (flag) (profiling-on |
09 | ( "Compile with profiling" )]) |
11 | [( "-o" "--optimize-1" ) |
12 | ,(lambda (flag) (optimize-level 1 )) |
13 | ( "Compile with optimization level 1" )] |
15 | ,(lambda (flag) (optimize-level 2 )) |
16 | (( "Compile with optimization level 2," |
17 | "which implies all optimizations of level 1" ))]) |
19 | [( "-l" "--link-flags" ) |
20 | ,(lambda (flag lf) (link-flags (cons lf (link-flags)))) |
21 | ( "Add a flag <lf> for the linker" "lf" )])) |
22 | (lambda (flag-accum file) file) |
这是神马浮云啊?括号套括号,看起来很厉害的样子,但又不是很明白。看到这样的设计,有的小伙伴连评价都懒得评价了,但也有的小伙伴对Lisp越发崇拜,表示Lisp就是所谓的终极语言了,没有哪门语言能写出这么不明觉历的代码来!小伙伴们正准备打完收工,突然…
5. Node.js的LineParser库
发现了Node.js的LineParser库:
04 | name : 'Android Debug Bridge' , |
06 | subcommands : [ 'connect' , 'disconnect' , 'install' ], |
09 | [ 'h' , 'help' , 'print program usage' ], |
10 | [ 'r' , 'reinstall' , 'reinstall package' ], |
11 | [ 'l' , 'localhost' , 'localhost' ] |
14 | [ null , 'host' , 'adb server hostname or IP address' , null ], |
15 | [ 'p' , 'port' , 'adb server port' , 5037 ] |
19 | [ 'connect' , [ 'host' , '[port]' ], null , 'connect to adb server' , adb_connect ], |
20 | [ 'connect' , [ 'l' ], null , 'connect to the local adb server' , adb_connect ], |
21 | [ 'disconnect' , null , null , 'disconnect from adb server' , adb_disconnect ], |
22 | [ 'install' , [ 'r' ], [ 'package' ], 'install package' , adb_install ], |
23 | [ null , [ 'h' ], null , 'help' , adb_help ], |
28 | var lineparser = require( 'lineparser' ); |
29 | var parser = lineparser.init(meta); |
31 | parser.parse([ 'install' , '-r' , '/pkgs/bird.apk' ]); |
天啊!?这是什么?我和小伙伴们彻底惊呆了!短短十几行代码就获得了上面5点的全面支持,重要的是小伙伴们居然一下子就看懂了,没有任何的遮遮掩掩
和故弄玄虚。本来以为Ruby和Lisp很酷,小伙伴们都想马上去学Ruby和Lisp了,看到这个代码之后怎么感觉前面全是在装呢?有个小伙伴居然激动
得哭着表示:我写代码多年,以为再也没有什么代码可以让我感动,没想到这段代码如此精妙,我不由得要赞叹了,实在是太漂亮了!
小伙伴们的故事讲完了,您看懂了吗?如果没有看懂的话,正题开始了:
在绝大多数语言中数据和代码可以说是泾渭分明,习惯C++、Java等主流语言的程序员很少去思考数据和代码之间的关系。与多数语言不同的是
Lisp以“数据即代码,代码即数据”著称,Lisp用S表达式统一了数据和代码的形式而独树一帜。Lisp奇怪的S表达式和复杂的宏系统让许多人都感到
Lisp很神秘,而多数Lisp教程要么强调函数式编程,要么鼓吹宏如何强大,反而掩盖了Lisp真正本质的东西,为此我曾写过一篇《Lisp的永恒之道》介绍Lisp思想。
设计思想和具体技术的区别在于前者往往可以在不同的环境中以不同的形式展现出来。比如,熟悉函数式编程的程序员在理解了纯函数的优点后即使是用C语
言也会更倾向于写出无副作用的函数来,这就是函数式思想在命令式环境的应用。所以,理解Lisp思想一定要能在非Lisp环境应用,才算是融汇贯通。
如果真正理解了Lisp的本质,那所谓的“数据即代码,代码即数据”一点儿也不神秘,这不就是我们每天打交道的配置文件吗!?如果你还不是很理解的话,我们通过下面几个问题慢慢分析:
1) 配置的本质是什么?为什么要在程序中使用配置文件?
不知道你是否意识到了,我们每天都在使用的各种各样的配置本质上是一种元数据也是一种DSL,这和Lisp基于
S表达式的“数据即代码,代码即数据”没有本质区别。在C++、Java等程序中引入配置文件的目的正是用DSL弥补通用语言表达能力和灵活性的不足。我
知道不少人喜欢从计算的角度来看到程序和语言,似乎只有图灵完备的语言如C++、Java、Python等才叫程序设计语言,而类似CSS和HTML这样
的东西根本不能叫做程序设计语言。其实,在我看来这种观点过于狭隘,程序的本质是语义的表达,而语义表达不一定要是计算。
2) 配置是数据还是代码?
很明显,Both!说配置是数据,因为它是声明式的描述,能方便地修改和传输;说配置是代码,因为它在表达逻辑,你的程序实际上就是配置的解释器。
3) 配置的格式是什么?
配置的格式是任意的,可以自己定义语法,只要配以相应的解释器就行。不过更简单通用的做法是基于XML、JSON、或S表达式等标准结构,在此之上
进一步定义schema。甚至完全不必是文件,在我们的项目中配置经常是放到用关系数据库中的。另外,下面我们还会看到用语言的Literal数据作为配置。
4) 业务逻辑都可以放到配置中吗?
这个问题的答案显然是:Yes!我没有遇到过不可以放入配置的逻辑,只是问题在于这样做是否值得,能达到什么效果。对于需要灵活变化,重复出现,有复用价值的东西放入作为配置是明智的选择。这篇文章的主要目的就在于介绍把主要业务逻辑都放到配置中,再通过程序解释执行配置的设计方法,我称之为:元驱动编程(Meta Driven Programming)。 |