(转)名词王国里的执行

原文是 steve-yegge 的神吐槽 https://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

译者:徐旭铭

“他们中的有些家伙的脾气可大了——特别是动词,最傲气就是他们——形容词可以随便捏,但动词就不行——不过他们都得听我调遣!不可捉摸!我就说这么多啦!”——矮胖子[1]

大家好!今天要讲的是邪恶的 Java 国王和他满世界消灭动词的故事。(首先要干掉的动词就是“消灭”(to stamp out),取而代之的是 VerbEliminatorFactory.create VerbEliminator(currentContext).operate()。不过现在说这个太早了,先卖个关子……)

警告:本文不是大团圆结局,也不适合心脏脆弱或喜欢无理取闹的人士阅读。若阁下火气比较大,总是忍不住在评论里挑事的话,也敬请离开。

故事开始前,首先让我们厘清一些概念。

垃圾的溢出[2]

既然 Java 程序员都喜欢“用例”,我们就先给出一个吧。比如,倒垃圾。再具体一点,就是:“强尼,快去倒垃圾!都快满出来啦!”

只要是正常说英语的普通人,在被要求描述倒垃圾这个行为时,基本思路都是这样的:

    get the garbage bag from under the sink
    carry it out to the garage
    dump it in the garbage can
    walk back inside
    wash your hands
    plop back down on the couch
    resume playing your video game (or whatever you were doing)
    “取出”水槽下的垃圾袋
    “拎出”车库
    “丢进”垃圾桶
    “走回来”
    “洗手”
    “坐回”沙发上
    “继续”打电动(或者之前在做的事)

就算你不懂英文,思考步骤应该也差不多,只不过用的是你最熟悉的语言罢了。不管用什么语言,也不管具体的步骤,只要你按部就班地去倒垃圾,最终的结果都是垃圾在外面,而你回到了屋里。

我们的脑子里充满了各种勇猛激进、热情洋溢的行为:呼吸,走路,交谈,欢笑,哭泣,希望,恐惧,吃喝,停停走走,倒垃圾。做什么和怎么表现都是我们的自由。假如我们只是太阳底下的顽石,或许生活不算糟糕,但绝不是自由的。自由来自我们自主行事的能力。

当然,我们的思想里也有名词。名词可以是吃的,从店里买的,用来坐的或睡的。“名词”会砸到你脑袋上,砸出一个大“名词”来。名词是事物,若连事物都没了,那我们如何自处?但名词也只是事物,仅此而已:它们能表示通向终点的方法,或者终点本身,或是什么珍宝,又或是周遭常见物体的名字。那是一栋楼。这里有块石头。连小孩子都能说出一大串名词。有趣的是发生在名词身上的“变化”,不是名词本身。

有行动才有变化,生命才会有滋有味。即便是滋味本身也离不开行动!毕竟不亲口“尝一尝”又怎么了解它们的味道呢。名词比比皆是,但让生活不断变化、多姿多彩的,还是动词。

除了动词和名词,还有形容词、介词、代词、冠词、回避不了的连词、好用的感叹词等其他各种可爱的词类,我们才得以思考和表达有趣的事物。我想没人会反对各种词类都各司其职,且同等重要,一个都不能少的观点。

假如有一天突然规定再也不许用动词了,难道不会让你觉得别扭吗?

接下来我要讲的故事就发生在这样一个地方……

名词王国

从前有个 Java 国,国王 Java 实行铁腕统治,生活在那里的人们是禁止像你我这样思考的。根据国王的法令,名词在 Java 国拥有举足轻重的地位,是王国里的一等公民。他们衣着华丽,招摇过市,这些都是由形容词提供的。尽管形容词远不及名词高贵,但他们也算安分守己,毕竟比起动词来,他们要幸运多了。

因为动词在这个王国里的日子是非常艰难的。

根据 Java 国王的法令,所有在 Java 国内的动词都为名词所有。这表示他们不仅仅是宠物这么简单,动词要负责 Java 国内全部的脏活累活。事实上他们就是王国的奴隶,最多也只是农奴或者奴仆。Java 国的居民总体上还是安于现状的,很少考虑生活能发生什么变化。

动词负责了 Java 国内的全部工作,但由于没人看得起他们,动词是被禁止闲逛的。只有在有名词陪同(escort)的情况下,动词才能出现在公共场合。

当然,“陪同”本身也是一个动词,是不可以单独行动的,所以必须获取(procure)所谓的动词陪护(Verb Escorter)来协助(facilitate)陪同。那“获取”和“协助”怎么办?这时就需要另外两个相当重要的名词,协助者(Facilitator)和获取者(Procurer),分别通过协助和获取的名词形式来监护下等动词“协助”和“获取”。

国王在Sun大神的指引下,多次威胁要彻底将动词驱逐出Java王国。假如真有那么一天,那他们肯定需要至少一个动词才能行动,而我们颇具黑色幽默的国王宣布,“执行”(execute)将是最合适的人选。

只要找到合适的执行者(Executioner)来调用 execute(),任何动词都可以被替换成动词“执行”,或是他的各种表亲“运行”(run),“开始”(start),“go”,“justDoIt”,“makeItSo”。等待(wait)可以变成 Waiter.execute() 。刷牙(brush)可以变成 ToothBrusher (myTeeth).go()。倒(take out)垃圾可以变成 TrashDisposalPlanExecutor.doIt()。没有动词可以幸免,全部会被名词取代。

在国内比较狂热的地方,名词已经彻底把动词给驱逐出去了。乍一看似乎动词依然到处都是,比如耕田、倒夜壶之类的。但只要仔细观察,就能看出端倪:名词把自己的 execute() 动词都包装成自己的样子,其实根本换汤不换药。看看 FieldTiller的 till(),ChamberPotEmptier的 empty() ,或者 RegisterationManager 的 register(),你会发现他们其实全都隶属邪恶国王的执行者大军,只是穿着自己名词主人的马甲罢了。

邻国的动词

在其他编程语言的王国里,倒垃圾是一件简单明了的事情,和英语里的说法没什么区别。和Java一样,数据对象是名词,函数则是动词。(变量名都有恰当的名字,属性是形容词,操作符一般作为连词,而不定参数表示的是“你们全部”等。不过这不是故事的重点。)

和Java国不同,其他王国的臣民可以自由按照符合自己要求的方式组合名词和动词。

比如,在邻邦 C 国、JavaScript 国、Perl 国和 Ruby 国里,倒垃圾由一系列动作组合而成——也就是所谓的动词,或者说函数。只要对适合的对象,按照正确的顺序执行这些动作(“取”垃圾,“拎”出去,“丢进”垃圾桶等),倒垃圾的任务就算成功完成,不需要什么多余的陪护或监护。

在这些王国,基本不需要硬造名词来束缚动词。他们没有 GarbageDisposalStrategy 和 GarbageDisposalDestinationLocator 这样的名词来找到倒垃圾的地点,也不需要 PostGarbageActionCallback 这样的名词来让自己倒完垃圾后坐回沙发。他们只要用动词去操作周围的名词,然后用一个归结性的动词 take_out_garbage() 就能让这些动作按照正确的顺序执行了。

这些邻邦通常会提供必要的机制,在需要的时候负责创建重要的名词。假如他们发明了一种全新的概念,比如房子、马车,或比人力快百倍的拉犁机械,他们就会为这个概念定义一个类,赋予它名字、描述、状态以及操作指令。

区别在于动词是可以独立存在的,无须发明新名词概念来存放它们。

Java 王国的人蔑视他们的邻居,这也是编程王国里的普遍现象。

若要寻根究底的话……

在世界的另一端是人烟稀少的地区,动词在那里拥有很高的地位。那些是函数式王国,有 Haskell、Ocaml、Scheme 等王国。那里的居民很少踏足 Java 周边的国家。由于周围没有什么其他国家,所以函数式王国只好相互鄙视,闲着没事的时候互相打个仗。

在函数式王国,名词和动词通常同属一个阶层。不过,名词就是名词,基本上整天啥事也不干。动词已经相当活跃,包办一切了,所以他们也就没有动力去操劳。反正也没有奇怪的法律规定一定要创造辅助性名词来监护动词,所以名词和王国里事物的数量是相等的。

因此,动词在这些国家可以翻手为云覆手为雨(请原谅我的用词)。在外人看来,很容易就会得出动词(比如函数)是一等公民的印象。这或许是它们叫函数式王国,而非东西的王国的原因吧。

在最遥远的国度,比函数式王国更远的地方,有一个传说中的地带叫做 Lambda the Ultimate。相传那个地方根本没有名词,只有动词!虽然那里有“东西”,但它们皆由动词创造。若传闻不虚,那地方连睡觉前数羊用的数字也不例外,而羊是当地最流行的货币。数字0就是lambda(),1则是 lambda(lambda()) ,2是 lambda(lambda(lambda())) ,依次类推。在这个传奇国度,每样东西,不管是名词、动词,还是其他什么,都是由 “lambda” 这个初始动词开始构建出来的。(据说动词 “lambda” 的本意就是 “去lambda” 。)

老实说,绝大多数 Java 国民很幸运地对世界另一边一无所知。不然你能想象得到那种文化冲击吧?他们会晕眩到必须发明新名词(比如“仇外”,“Xenophobia”)来表达自己的新情绪。

Java 国的人们快乐吗?

你可能会觉得 Java 国的生活说好听点是奇怪,说难听点就是效率低下。不过一个社会的幸福指数可以通过儿歌窥探一二,而 Java 国的儿歌确实有点古怪、诗意。例如,Java 国的小孩常常会背诵这样一首非常有名的警世寓言:

    For the lack of a nail,
       throw new HorseshoeNailNotFoundException("no nails!");
    For the lack of horseshoe,
       EquestrianDoctor.getLocalInstance().getHorseDispatcher().shoot();
    For the lack of a horse,
       RidersGuild.getRiderNotificationSubscriberList().getBroad- caster().run(
        new BroadcasterMessage(StableFactory.getNullHorseInstance()));
    For the lack of a rider,
       MessageDeliverySubsystem.getLogger().logDeliveryFailure(
        MessageFactory.getAbstractMessageInstance(
         new MessageMedium(MessageType.VERBAL),
         new MessageTransport(MessageTransportType.MOUNTED_RIDER),
         new MessageSessionDestination(BattleManager.getRoutingInfo(
                           BattleLocation.NEAREST))),
        MessageFailureReasonCode.UNKNOWN_RIDER_FAILURE);
    For the lack of a message,
       ((BattleNotificationSender)
        BattleResourceMediator.getMediatorInstance().getResource(
         BattleParticipant.PROXY_PARTICIPANT,
         BattleResource.BATTLE_NOTIFICATION_SENDER)).sendNotification(
          ((BattleNotificationBuilder)
           (BattleResourceMediator.getMediatorInstance().getResource(
           BattleOrganizer.getBattleParticipant(Battle.Participant. GOOD_GUYS),
           BattleResource.BATTLE_NOTIFICATION_BUILDER))).build- Notification(
            BattleOrganizer.getBattleState(BattleResult.BATTLE_ LOST),
            BattleManager.getChainOfCommand().getCommandChain Notifier()));
    For the lack of a battle,
       try {
         synchronized(BattleInformationRouterLock.getLockInstance()) {
          BattleInformationRouterLock.getLockInstance.wait();
         }
       } catch (InterruptedException ix) {
        if (BattleSessionManager.getBattleStatus(
          BattleResource.getLocalizedBattleResource(Locale. getDefault()),
          BattleContext.createContext(
           Kingdom.getMasterBattleCoordinatorInstance(
            new TweedleBeetlePuddlePaddleBattle()).populate(
             RegionManager.getArmpitProvince(Armpit.LEFTMOST)))) ==
          BattleStatus.LOST) {
         if (LOGGER.isLoggable(Level.TOTALLY_SCREWED)) {
          LOGGER.logScrewage(BattleLogger.createBattleLogMessage(
           BattleStatusFormatter.format(BattleStatus.LOST_WAR,
                            Local.getDefault())));
         }
        }
       }
    For the lack of a war,
       new ServiceExecutionJoinPoint(
        DistributedQueryAnalyzer.forwardQueryResult(
         NotificationSchemaManager.getAbstractSchemaMapper(
          new PublishSubscribeNotificationSchema()).getSchemaProxy().
           executePublishSubscribeQueryPlan(
            NotificationSchema.ALERT,
            new NotificationSchemaPriority(SchemaPriority.MAX_ PRIORITY),
            new PublisherMessage(MessageFactory.getAbstractMessage(
              MessageType.WRITTEN,
              new MessageTransport(MessageTransportType.WOUNDED_ SURVIVOR),
              new MessageSessionDestination(
               DestinationManager.getNullDestinationForQuery Plan()))),
            DistributedWarMachine.getPartyRoleManager().get RegisteredParties(
             PartyRoleManager.PARTY_KING ||
             PartyRoleManager.PARTY_GENERAL ||
             PartyRoleManager.PARTY_AMBASSADOR)).getQuery Result(),
         PriorityMessageDispatcher.getPriorityDispatchInstance())).
        waitForService();
    All for the lack of a horseshoe nail.

时至今日,这也是金玉良言。

虽然 Java 国的版本和本·富兰克林的原版在叙述上略有不同,但他们还是觉得自己的版本别有一番风味。

其中特点之一就是凸显了“架构”。Java 国王授予了架构尊崇的地位,因为架构完全是由名词组成的。我们都知道,名词就是事物,而在 Java 国里,事物的地位远胜一切动作。建筑[3]是由看得见摸得着的事物构成的,譬如高耸入云的庞然大物,又如用棍子敲打时发出低沉悦耳声音的东西。Java 国王特别喜欢这种沉闷的声音,每次换新马车的时候,他都特别喜欢从踢轮子中获得快感。不管上述的儿歌有何瑕疵,它就是不想要任何东西。

为躲避风吹雨打,人类会本能地寻找庇护所。庇护所越强大,我们就越有安全感。Java国有很多这样坚固的东西可以满足国民安全感。他们在惊叹宏伟架构的同时会觉得这样的设计“一定是非常了不起的”。当需要修改结构时,这种感觉尤为强烈。架构上的强度过于惊人,没人相信这样的结构会倾倒。

除了坚固的架构,Java 国的一切都有条不紊:所有名词都各得其所。所有故事都是一个套路:创建对象自然是重中之重,每层抽象离不开一个管家(manager),每个管家都有一个run()方法。只要稍微训练一下这种风格的概念建模,Java 国的人们发现不管什么对象都可以这样表达。任何抽象和计算都可以表达出来,这就是所谓的“名词演算”。只要有足够的名词以及名词构造函数、遍历名词图表的访问方法,还有负责执行计划的execute()就行了。

Java国的居民不光幸福——他们的自豪感简直是爆棚!

StateManager.getConsiderationSetter(“Noun Oriented Thinking”, State. HARMFUL).run()

按Java国国外的话来说就是:“面向名词的思考方式是有害的。”

面向对象编程将名词的地位捧上了天。可为什么费那么大劲把语言中的一部分请上神坛?为什么一种概念比另一种优异?其实并非OOP突然将我们思考方式里动词的地位降低了。它其实是一种扭曲了的观点。我的朋友雅各布·加布里尔森(Jacob Gabrielson)曾经说过,推崇面向对象编程就好像推崇面向裤子穿衣服一样。

Java 的静态类型系统和其他语言一样,有些毛病是共通的。只是过分强调面向名词的思考方式(以及建模方式)确实有点让人讨厌。任何类型系统都会要求你改变一下思维方式来适应它的系统,但是彻底消除单独的动词似乎也太过了。

C++ 不存在这个问题,因为 C++ 是 C 的超集,你可以单独定义函数。另外,C++ 专门提供了命名空间的抽象,而Java则重载了类的概念,用来表示命名空间、自定义类型、语法委托机制、一部分可见性和作用域机制,不一而足。

千万别误会——我没有说 C++“好”。但我确实更喜欢其类型系统之灵活性,至少比 Java 好一点。C++ 的问题在于就算是普通的句子,也会让听众忍不住想要扑上来杀人(比如,意料之外的segfault等各种一不小心就会踩进去的陷阱),用 C++ 表达某个特定想法的时候,有时找到正确方式的难度高得惊人。但是它表达思想的简洁能力却远胜 Java,因为 C++ 里有动词,谁会想要用没有动词的语言啊?

事实上类是 Java 里唯一的建模工具。每当新主意出现的时候,你就要通过打磨、封装、敲打的办法硬把它变成一个什么东西,哪怕它原来只是一个动作、流程,或是其他什么不是“东西”的概念。

我又想起了八九年前那些玩 Perl 的朋友跟我说的话:“老兄,不是所有的东西都是对象。”

不过奇怪的是,Java(好吧,C#也算一个,谁让它们那么像呢)似乎是主流面向对象语言里唯一一个表现出狂热的以名词为中心行为的语言。在Python和Ruby里就几乎完全没有 AbstractProxyMediator、NotificationStrategyFactory 这种东西的存在。

那为什么 Java 里那么泛滥呢?肯定是动词上的区别造成的。Python、Ruby、JavaScript、Perl 以及所有的函数式语言都可以把函数当做单独的实体来声明和传递,而无须将它们包裹在类里。

在动态类型的语言里这当然更加容易,你只要传递函数的引用(通过函数名获取),调用者自会用正确的参数来调用函数,并正确处理返回值。

可很多静态类型语言里也都把函数作为基本对象。这其中就包含了类型啰唆的C和C++,另外还有类型推导的 Haskell 和 ML 。作为语言,只要提供创建,传递,按照恰当的类型签名来调用函数字面量的机制就可以了。

Java也完全可以引入函数亦对象概念,从而变得成熟,不再扭曲,让大家可以在思维过程里使用动词。事实上有一门名叫“The Nice programming language”的JVM语言 ,它采用类似 Java 的语法,同时还提供了使用动词的机制:独立函数,Java会迫使你用 Callback、Runnable,或其他匿名借口实现类来包裹它,以便调用。

Sun 甚至不必打破要求所有函数都要属于类的规定,只要匿名函数携带一个隐含的“this”指针指向定义它的类就可以了。

我不知道为什么 Sun 坚持把 Java 圈在名词王国里。我不觉得是因为他们低估程序员能力的问题。他们连泛型都加了进来,这可是一个复杂好多倍的概念,所以他们显然也不再打算保持语言简洁。增加复杂度也不一定是坏事,毕竟 Java 现在已经稳定下来了。给程序员提供更多的工具,让他们能按照自己的方式编程才更有意义。

我真心实意地希望他们能修复这个问题,这样我就能倒完垃圾后回来继续打电动,或继续刚刚在做的事情了。

脚注


  1. 见《爱丽丝镜中奇缘》第6章 ↩︎

  2. 原文是 garbage overfloweth, 这里是故意这样翻译的,以便强调“溢出”在这里是个名词 ↩︎

  3. 作者这里在玩文字游戏,architecture 既是架构,又是建筑 ↩︎

posted @ 2018-12-24 18:59  JOJO_2046  阅读(316)  评论(0)    收藏  举报