如何设计一门编程语言(四) 什么是坑(操作模板)

其实我在写这个系列的第三篇文章的时候就已经发现,距离机器越远,也就是抽象越高的概念,坑的数量是越少的。但是这并不是说,距离机器越近的概念就越强大或者说越接近本质。这是广大的程序员对计算理论的一种误解。大多数人理解编程的知识结构的时候,都是用还原论来理解的,这个方法其实并没有错。但问题在于,“还原”的方法并不是唯一的。很多人觉得,反正你多高级的语言编译完了无非都是机器码嘛。但是还有另一种解释,你无论多低级的语言编译完了无非也就是带CPS变换(continuation passing style)的λ-calculus程序嘛。他们是等价的,不仅能力上也是,“本质”上也是。

一个用CPS变换完整地处理过的λ-calculus程序长的就很像一串指令。而且类似于C++的inline操作,在这里是完全自然、安全、容易做的。那其实为什么我们的机器不发明成这样子呢?显然这完全跟我们想如何写一个程序是没关系的。正是这种冲突让我们有一种“概念距离机器越远运行速度就越慢”的错误的直觉。扯远了讲,就算你在用一门函数式语言,譬如说Haskell也好,F#也好,最终在运行的时候,还是在运行彻底编译出来的机器码。这些语言是完全不需要“模拟器”的,虽然由于各种历史原因人们首先开发了模拟器。当然一个精心设计过的C程序肯定还是要比haskell快的,但是我觉得能这么干的人不多,而且大多数时候这么干都是在浪费老板的钱而已,因为你们的程序原本就不需要快到那种份上。这种东西就跟那些做互联网对于测试的想法是一样的——有bug?发现了再说,先release抢市场。

如果对这方面有了解的话,CPS变换——也就是Lost In Stupid Parentheses-er们最喜欢的call-with-current-continuation,他的另一个名字叫call/cc——是一种跟goto一样强大而且基本的控制流的做法。goto和CPS可以互相转换不说了,所有其它控制流都可以转换成goto和CPS。它们两者在这方面是不相上下的。而且既然一个完全用CPS变换处理过的程序长得就像一串指令,那你说他们的区别是什么呢?区别就是,CPS可以是强类型的,而goto则永远都不可能。

作为废话的最后一段,我给个小例子来讲什么叫“一个用CPS变换完整地处理过的λ-calculus程序长的就很像一串指令”。就让我们用a(b( x ), c( x ))这样的一个表达式来讲:
处理前:

a (b x) (c x)

处理后:

b x λa0.
a a0 λa1.
c x λa2.
a1 a2

用我们熟悉到不能再熟悉的Haskell的Monad的手法来翻译一下其实就是:

a0 <- b(x)
a1 <- a(a0)
a2 <- c(x)
return (a1(a2))

好了,至于上面这种形式(看起来很像SSA)是怎么被做成机器码的,大家自己去看编译原理吧。上面这么多废话就是想表达一个结论:抽象并不意味着负担。当然,至于对程序员的智商上的要求,对某些人也是一种负担,这个我就没办法了,所以就不考虑他了。

===============废话结束================

模板也是这类抽象的一种。为什么我要把标题写成“坑”,只是想跟前面统一一下而已,其实到了模板这么高级的抽象的时候,基本上已经没什么坑了。当然C++的做法就另当别论了,而且我想那些坑你们大概一辈子也碰不到的了。那我们先从简单的讲起。

比模板更简单的东西自然就是泛型了。为什么叫他泛型?因为泛型实际上就是一种复制代码的方法,它本身是没有推导能力的,所以肯定谈不上什么模板了。但是在大多数情况下,泛型这么弱的抽象也已经基本够用了。跟泛型相关的手法大约有三个。

第一个就是定义一个返回一个类的函数(在这里参数是T):

class Array<T>
{
   public Array(int count);
   public int Count{get;}
   public T this[int index]{get; set;}
}

时间: 2024-08-22 14:43:28

如何设计一门编程语言(四) 什么是坑(操作模板)的相关文章

如何设计一门编程语言(二) 什么是坑(b)

我从来没有在别的语言的粉里面看见过这么容易展示人性丑陋一面的粉,就算是从十几年前开始的C++和C对喷,GC和非GC对喷,静态类型动态类型对喷的时候,甚至是云风出来喷C++黑得那么惊天动地的时候,都没有发生过这么脑残的事情.这种事情只发生在go语言的脑残粉的身上,这究竟代表什么呢?想学go语言的人最好小心一点了,学怎么用go没关系,go学成了因为受不了跳到别的语言去也没关系,就算是抖M很喜欢被折腾所以坚持用go也没关系,但是把自己学成了脑残粉,自己的心智发生不可逆转的变换,那就不好了. 当然,上一

如何设计一门编程语言(一) 什么是坑(a)

这个系列的起因是这样的,王垠写了一篇喷go的博客http://www.yinwang.org/blog-cn/2013/04/24/go-language/,里面说go已经烂到无可救药了,已经懒得说了,所以让大家去看http://www.mindomo.com/view.htm?m=8cc4f95228f942f8886106d876d1b041,里面有详细的解释.然后这篇东西被发上了微博,很多博友立刻展示了人性丑陋的一面: 1.那些go的拥护者们,因为go被喷了,就觉得自己的人格受到了侮辱一样

如何设计一门编程语言(九) 类型

类型是了解编程语言的重要一环.就算是你喜欢动态类型语言,为了想实现一个靠谱的东西,那也必须了解类型.举个简单的例子,我们都知道+和-是对称的--当然这只是我们的愿望了,在javascript里面,"1"+2和"1"-2就不是一回事.这就是由于不了解类型的操作而犯下的一些滑稽的错误.什么,你觉得因为"1"的类型是string所以"1"+2就应该是"12"?啐!"1"的类型是(string

如何设计一门编程语言(十) 正则表达式与领域特定语言(DSL)

几个月前就一直有博友关心DSL的问题,于是我想一想,我在gac.codeplex.com里面也创建了一些DSL,于是今天就来说一说这个事情. 创建DSL恐怕是很多人第一次设计一门语言的经历,很少有人一开始上来就设计通用语言的.我自己第一次做这种事情是在高中写这个傻逼ARPG的时候了.当时做了一个超简单的脚本语言,长的就跟汇编差不多,虽然每一个指令都写成了调用函数的形态.虽然这个游戏需要脚本在剧情里面控制一些人物的走动什么的,但是所幸并不复杂,于是还是完成了任务.一眨眼10年过去了,现在在写Gac

如何设计一门编程语言(三) 什么是坑(面向对象和异常处理)

在所有的文字之前,我需要强调一下,我本人对structure typing持反对态度,所以就算文中的内容"看起来很像"go的interface,读者们也最好不要觉得我是在赞扬go的interface.我比较喜欢的是haskell和rust的那种手法.可惜rust跟go一样恨不得把所有的单词都缩成最短,结果代码写出来连可读性都没有了,单词都变成了符号.如果rust把那乱七八糟的指针设计和go的那种屎缩写一起干掉的话,我一定会很喜欢rust的.同理,COM这个东西设计得真是太他妈正确了,简

如何设计一门编程语言(五) 面向对象和消息发送

面向对象这个抽象的特例总是有说不完的话题,更糟糕的是很多语言都错误地实现了面向对象--class居然可以当一个变量类型什么的这只是让人们写代码写的更糟糕而已.当然这个话题第三篇文章已经说过了,现在来谈谈人们喜欢拿来装逼的另一个话题--消息发送. 按照惯例先来点题外话.说到消息发送,有些人喜欢跳出来说,objective-c的消息做得多优雅啊,代码都可以写成一句话[golang screw:you you:suck]之类的.其实这个还做得不够彻底.在几年前易语言曾经火了一阵,但是为什么大家这么讨厌

如何设计一门编程语言(八) 异步编程和CPS变换

关于这个话题,其实在(六)里面已经讨论了一半了.学过Haskell的都知道,这个世界上很多东西都可以用monad和comonad来把一些复杂的代码给抽象成简单的.一看就懂的形式.他们的区别,就像用js做一个复杂的带着几层循环的动画,直接写出来和用jquery的"回调"写出来的代码一样.前者能看不能用,后者能用不能看.那有没有什么又能用又能看的呢?我目前只能在Haskell.C#和F#里面看到.至于说为什么,当然是因为他们都支持了monad和comonad.只不过C#作为一门不把&quo

如何设计一门编程语言(十一) 删减语言的功能

大家看到这个标题肯定会欢呼雀跃了,以为功能少的语言就容易学.其实完全不是这样的.功能少的语言如果还适用范围广,那所有的概念必定是正交的,最后就会变得跟数学一样.数学的概念很正交吧,正交的东西都特别抽象,一点都不直观的.不信?出门转左看Haskell,还有抽象代数.因此删减语言的功能是需要高超的技巧的,这跟大家想的,还有跟go那帮人想的,可以断定完全不一样. 首先,我们要知道到底为什么需要删减功能.在这里我们首先要达成一个共识--人都是很贱的.一方面在发表言论的时候光面堂皇的表示,要以需求变更和可

如何设计一门编程语言(七) 闭包、lambda和interface

人们都很喜欢讨论闭包这个概念.其实这个概念对于写代码来讲一点用都没有,写代码只需要掌握好lambda表达式和class+interface的语义就行了.基本上只有在写编译器和虚拟机的时候才需要管什么是闭包.不过因为系列文章主题的缘故,在这里我就跟大家讲一下闭包是什么东西.在理解闭包之前,我们得先理解一些常见的argument passing和symbol resolving的规则. 首先第一个就是call by value了.这个规则我们大家都很熟悉,因为流行的语言都是这么做的.大家还记得刚开始