代码转换是一座金矿

沙枣 发布于 2016/09/08 16:27
阅读 1K+
收藏 4

计算机语言的世界有一个奇怪的现象:不同的语言建立的国度彼此能够沟通的东西太少:

用 Java 写的函数,模块,框架,如果想在 Go 应用环境中使用,那么就要重新书写。虽然 C 语言书写的代码在一些个别的语言环境中可以使用,但这样的语言实在不多。

根据贸易的原则,如果 A 地生产一种商品比 B 地生产这种商品成本低,如果从 A 地将这种产品运到 B 地进行销售,那么就会赚更多的钱。

同理,在计算机世界中,如果可以用现成的函数,模块,框架,通常是不会去重新开发同样功能的东西,除非是想学习或者是现成的东西无法满足需求。

大部分语言都是全能型语言,在许多领域都开发了功能近似,名称相近的函数库和各种框架。而这些东西能够通用的却很少。

如果能够将 A 语言开发的优秀的函数,模块或框架用一种技术转换成 B 语言表达的代码,那么就会创造巨大的价值,而这种技术也会非常受欢迎。这种技术就是代码转换。

代码转换需要的技术很多:代码解析,语法树转换,语言特性映射,代码类型检测,符号表检查等许多技能。而现有理论系统,在处理代码解析编译方面非常复杂,让大多数程序员望而却步。

其实这个领域没有想象中那么难以涉足,Lisp Perl6 LLVM 给了我们一些启示:

首先说说 LLVM, LLVM 成功了,因为它让许多语言设计的程序员从底层各种 CPU 架构的汇编语言中解脱出来,用一种 LLVM IR 的汇编语言,就可以自动转换成许许多多 CPU 架构上快速运行的机器码。而一些喜欢研究各种汇编语言和 CPU 架构的程序员也非常欢喜,因为借助 LLVM, 他们的工作得到更多人的认可。

Perl6 没有成功,但她吸引了许多优秀的程序员长期的聚集在一起,日以继夜的合作,她的吸引力在哪里呢?就是语法分离技术。

几乎所有现有语言的架构,语言的语法信息和语法树构建,代码编译是一体的,你如果感觉 Python 中没有 switch, case 很不方便,建议开发者增加这个功能,那么得到答复的可能性非常小,即使得到答复,也是拒绝。围绕 print 是关键字还是函数的问题,Python 分化成两个版本,就是证明:语言修改现有的语法是非常困难的,虽然从兼容以前版本上有坚持的道理,但从技术上,需要改动的东西太多了,就好像从一个盖好的巨大积木的底部,抽掉一块。这会导致非常多的连锁反应。

Perl6 将语言的语法单独分离出来,成为一个可以随时改动的配置文件。这让 Perl6 敢定义非常非常多的语法特性,而不担心维护问题。

这种技术带给我们一种全新的语言架构,让语言的解析变得不那么复杂。因为这种技术将代码解析这个最复杂的问题变成了两个相对独立,简单的问题:语法定义和语法树解析。

传统的 bison + yacc 是将语法定义和语法树构建合在了一起,让这个问题变得复杂。

最后一种技术是从 Lisp 中得到的:符号解析。

符号解析代替了语法树解析的传统思路,他将所有的 token 看成是符号,符号可以 eval, 也可以不 eval. 符号不仅仅是变量,他是一个可以代表变量,函数名称,包名称,关键字,操作符等所有在代码中出现东西的名字,这种抽象大大简化了语言语法树的解析难度。

在 Lisp 中,所有的特殊句法:if, switch, func, while, package, public 都是宏,也就是暂时不 eval 的列表。这种抽象也让代码解析可以分解成若干个简单一些的问题。

利用这些技术,分析程序代码,解析并且转换就不是高大尚的技术领域,而是普通程序员都可以尝试的小玩意。当然,抽象思维的挑战是必须的。因为转换不同的语言,要对语言特性有深入的理解,看清楚不同语法背后相同的原型和实现机制。这对学习和接受一门新的语言很有帮助。

如果不同语言社区的优秀代码可以互相交换,那将会创造多大的价值?

欢迎有相同看法的程序员发表自己的看法。

加载中
1
ling0
ling0
不错的帖子,难得见到一个搞编程语言的。
看法和你有些出入的可以回复吧,在回复之前,先来看一下小故事吧。
// 故事开始
碳基人在墓志铭上说,上古时代的程序员最初都在用同一种编程语言。他们在 0 和 1 与沙子之间,发现了一块非常肥沃的土地,于是就在那里定居下来,修起了城池。后来,他们的日子越过越好,决定修建一张覆盖全世界的网,这就是后来的天网。他们用 0 和 1 作为建筑的材料。直到有一天,依附于沙子,天网即将完成。神得知此事,他非常愤怒,认为这是程序员虚荣心的象征。神心想,程序员们讲同样的语言,就能建起这样的系统,以后还有什么办不成的事情呢?于是,神决定让程序员的语言发生混乱,使程序员们互相语言不通。后来,程序员们就把哪种编程语言最好的讨论叫做”圣战”。

然而,许多年以后,随着程序员们的努力,各种编程语言的差异越来越小,各种编程语言代码开始向 gayhub 聚集,天网随之开始慢慢进化...

多年以后,面对硅基人的屠刀,人类将会想起程序员带他们去见识大一统超级语言 NIL Language 的那个遥远的下午。

那一天,程序员们终于回想起,曾经一度被他们支配的愤怒,还有那论坛里讨论哪个编程语言是最牛X的圣战时激情。
// 故事结束

以前有一段时间,我也有过这样的想法,后来这样的想法越来越淡,或者说不再想这件事。
你说的现象没有什么奇怪的,编程语言是人与计算机沟通的工具,或者在一定程序上也可以说是程序员之间交流的工具。计算机世界和现实世界一样丰富多彩,让全世界的人都说同一种语言是不可能的。所以你在帖子里说了一种代码转换。

我们就来说说代码转换,你知道翻译的时候一个难点是什么?不是说你把一种语言翻成另一种语言别人就一定能看懂。语言的存在是有一个大大的语境的,比如一个中文笑话,或者字谜,你翻成英文让一个英文土著去看,如果文化相关性强的化,他可能看不懂。可是明明每个单词他都认识。别说英文,就是中文之间,不同的时期,不同的地域,也有差异。可是明明都是中文。

同样用 C++,每家公司的 C++ 代码都有自己的味道,每个程序员不同时期的代码也都有不同的味道。为什么?

每种语言的出现,都是为了解决某种问题。这就是它的文化,随着用户的增多,需求的变化,它也在不停的进化。一个编程语言最后总是要解决所有的问题,包括那些不存在的和由它自己产生的,的可计算性,图灵等价说得就是这些。

很多小的编程语言的出现,可能开始仅仅是为了解决一个很小的问题,或者语言的创造者觉得没有趁手的工具,所以他要写一个。君不见,每个程序员都有自己趁手的轮子,如果没有,那就造一个,看看 gayhub 就知道了。编程语言这么多的范式,模式,框架,约定,是自己给自己找麻烦么?

语言的多性样的根本原因是生物的多样性,问题的多样性,每个人都是不同的,为什么要让 A 必须用 B 发明的轮子,写的语言?

好,既然每个人都不想改变,那就搞个转换器。你知道在 gayhub 有多少个这种工具么?靠卖这种工具开公司的不在少数。但是, 这解决了根本问题了么?需求是在变化的,需求唯一的不变就是变化。

一个公司的技术选型和最早的那个程序员有关,和技术领袖的个人喜好有关。而人也是每个都不同的。好吧,假设接了一个烂盘子,那个语言我不会啊,找个工具转一下,后来发现,还是自已写吧。程序员,一言不合就重构,再言不合就重写,再言不合就换语言,难道不是么?

你知道 Perl 为啥死了,lisp 万古留芳么!因为他们太牛 B 了必须死。Perl 说,我自己发起狠来连我自己都不认识。Lisp 说,不管你是否在我之前出生,我必须是你的爸爸!好吧,你们这么牛 B,就让你们留在传说之中吧。

语言是一种信仰,要不然,程序员哪来那么多圣战!

当然了,不排除将来有一天,出现这么超级无敌牛 B 的程序,可能和人类也没啥关系了。反正硅基人只看二进制就足够了。

根据熵增定律,失控才是常态。

所有的语言最后都会变成 lisp,当然了,也可能是 c++,Perl...

以上,由于时间关系,瞎写了一点儿。失误之处还请见谅。

不说了,工头让我去搬砖了。

1
k
kchr

你的很多前提是想当然的,是错误的。比如

“根据贸易的原则,如果 A 地生产一种商品比 B 地生产这种商品成本低,如果从 A 地将这种产品运到 B 地进行销售,那么就会赚更多的钱。”

“同理,在计算机世界中,如果可以用现成的函数,模块,框架,通常是不会去重新开发同样功能的东西,除非是想学习或者是现成的东西无法满足需求。”

"程序员有个不变的特性,能够少写字符,不影响程序可读性的话,99%的会选择较短的方式。"

现实世界不是这样的。

沙枣
沙枣
现实世界有很多特殊现象,奢侈品比普通商品更容易赚钱。老板希望代码稳定,文档齐全,代码越多,显得程序员工作效果多。有些程序员经常说,写过多少个几十万行代码的项目,以此表示自己经验丰富。而不说多少行抄的,多少行注释,用什么语言,有没有同类模块什么的。
0
乌龟壳
乌龟壳
代码转换到能跑起来小case而已,但是保留缩进表达的意义,注释等就呵呵了。一般来说解决一个问题并不需要太多的代码,君不见很多c程序全部轮子自己造吗,就算外面有无穷的别人用c写好的同样功能。
乌龟壳
乌龟壳
回复 @沙枣 : 给你说一些案例可能比较好描述,.net平台的c#/vb.net/f#等都能互相调用。python可以调用c的dll,只要定义一个类,类型是ctype啥的就行了。规范好了沟通就没问题了。
乌龟壳
乌龟壳
回复 @沙枣 : 这只要制定好规范保证相同就行了,比如规定php的array('a' =>1)对应struct { int a}即可
沙枣
沙枣
@乌龟壳 二进制调用,要进行类型映射,有指针,有结构,有数组,也不能共享变量。因为编译会将所有的变量名称映射成寄存器地址,而且是随机分布。在一种语言中调用另外一种语言和在同一种语言中调用另外一个模块,有天壤之别。
沙枣
沙枣
@乌龟壳 每种语言的基本数据结构不同,映射成 IR 后的数据结构实现也有差别。
乌龟壳
乌龟壳
回复 @沙枣 : 其实你想说的是在代码层面产生互相调用的效果吗?我说的是二进制层面,如c写的dll可以在python中调用
下一页
0
乌龟壳
乌龟壳
做这个说白了想解决重用的问题,一般来说你做个http服务就结了,多少种语言都能互相调用,微服务大概就是这个意思
乌龟壳
乌龟壳
回复 @沙枣 : 没错,所以你文末的【约定】做好后,就能互相使用对方的逻辑了,约定至少比具体逻辑要简单吧:-)
沙枣
沙枣
@乌龟壳 你的算法也许是这样的,接受一个Json字符串作为参数,并提供一个服务的名称,然后在内部将Json数据解析成某个函数的参数,进行简单的类型映射,因为Json只支持Hash,Array,String.然后把结果解析成按照约定的 Json 数据,其实这就是函数的映射,不过是转换的是 Json,Json 是一种描述简单数据的语言, 如果要描述比较复杂的数据结构,就要事先进行约定,哪个字段代表什么意思。
乌龟壳
乌龟壳
回复 @沙枣 : 我说的不是代码转换,例如我用自己开发的语言做了个算法,然后提供个http接口,这样别的语言就能用上了,何必转换代码
沙枣
沙枣
http 的 resful 服务可以允许多种语言调用,但归根结底要在算法上实现,才能用这种方式提供代码转换的服务。
0
乌龟壳
乌龟壳
还有就是动态语言转静态语言,一般都只能转成一坨坨哈希表操作代码,因为动态语言结构性很弱。js的v8花了多大力气才优化出一些静态语言的效果
乌龟壳
乌龟壳
回复 @沙枣 : 动态语言静态化本来就是jit要做的优化之一。
沙枣
沙枣
把动态语言转换成 Nodejs 看样能提高效率。其实很多语言都在做这件事情,把动态语言特性的代码编译成静态的二进制。例如 Julia, 完全可以利用这些平台,将动态语言转换成 Julia, 然后在转换成静态语言。
0
沙枣
沙枣

to: ling1

看样你对人性的弱点有深入了解,确实,人喜欢多样化和个性化,不同的语言的特别之处很多,很多和发明语言的人有关。你的故事就是巴别塔故事的隐射。

就好像 Lua, 数组索引从 1 开始,这本是很符合人类常识的,但由于大部分语言是从 0 开始的,所以很多人讨厌这种设计,有人干脆把 Lua 底层从新改写,发明了另外一种语言。

我记得好像 <<程序设计语言实践之路>>中说,很多语言的产生极具偶然性,完全没有按照需求来。

做这件事情要想满足多变的需求,确实很难。但我认为是可行的,也相信能够完成。

如果要转换一种语言,没有必要映射这种语言所有的特性,就和 <<Javascript 语言精粹>>的作者说的,每种语言都有精华和糟粕,完全可以用精华部分写出健壮的代码,所以,映射的部分只是语言中很少语法特性,这似乎没什么用,毕竟大部分代码都是普通程序员写的代码,其实对于“糟粕”特性,完全可以采用“提示”的方式,毕竟要转换的是优质的代码库。

很多语言的产生顺应了程序员的需求,coffeescript 就是顺应了 Javascript 繁琐的迭代数组写法,没有块作用域等很多问题产生的,而 Elixlr 就是因为 Erlang 语法太过生涩而诞生的。Java 虚拟机上为什么有那么多的语言解释器:Clojure, JRuby 就是因为 Java 太麻烦了,但库函数又太丰富了。

程序员有个不变的特性,能够少写字符,不影响程序可读性的话,99%的会选择较短的方式。

所以,如果要做这件事情,要首先定义一种非常简洁的语言规范。这个语言规范不需要用解释器实现,只是将这种规范写出的代码转换成其他语言即可。

返回顶部
顶部