Skip to content

四火的唠叨

一个纯正程序员的啰嗦

Menu
  • 所有文章
  • About Me
  • 关于四火
  • 旅行映像
  • 独立游戏
  • 资源链接
Menu

从 Java 和 JavaScript 来学习 Haskell 和 Groovy(元编程)

Posted on 06/26/201510/08/2024 by 四火

metaProgramming

本篇文章的话题是元编程。首先来认识元编程,我在第一篇 《引子》里面已经介绍:元编程,指的是在运行时改变 “类” 的定义,例如访问、增加或修改等等。一言以蔽之,就是 “用程序来写程序”。在第二篇的 《类型系统》里面已经借由继承和接口的实现,介绍了一些利用元编程特性来增加或改变子类行为的方法。回顾语言发展的长河,其实是经历了一个从 “对象 -> 类 -> 元类” 到 “对象 -> 原型” 的发展过程的。所以,无论是类,还是元类,这样的概念其实都不是非有不可的,只是因为我们思考的习惯,特别是抽象的习惯而顺其自然地产生了。这一点我在 《编程范型:工具的选择》里面已经详细描述了,建议在往下阅读前移步。

 

正式介入元编程的部分,先来看看 Java,它的方式比较原始,也比较清晰,本身它定义了 Class、Method、Field 等等描述一个类的基本概念,基于静态语言的限制,没有办法真正在运行时改变一个类内部的结构(但是可以在运行时获取一个类内部的结构),于是有了像 CGLib 这样在运行时使用动态代理,创建一个类来替代的办法,让使用者看起来好像是改变了原始类的结构。当然,在编译期,像 AspectJ 这样的工具可以做到真正的 “织入” 逻辑,控制字节码的生成。对于 Java 的元编程本身而言,即便到今天,局限性很大,但是局限性并不意味着有用性,可以说如今元编程的应用已经铺天盖地,其中有这样两件事情大大加速了它元编程的发展:

  • 一是 JDK 5 的注解,虽说它和元编程本身没有直接的联系,但是它提供了一种便捷的代码修饰方式,也让对于既有代码的扩展变得方便而充满可能。比如像 Lombok 这样基于注解的类库,让一个类的扩展和完善非常容易。
  • 二是 Spring,无论是学 J2ME 还是 J2EE,Spring 都是值得去了解的,AOP 的概念老早就提了,但就是从它开始发扬光大的;IoC,把对象管理和拼装的逻辑反转到业务逻辑之外的容器上,这些实现都是需要通过对元编程的操纵来完成的。

 

再来看看 Haskell,把它和 Java 放在一起介绍,因为二者都是静态语言,改变类或者定义结构的事情只能寄期望于编译期完成。Haskell 的元编程并非核心内容,因此也更加初级,据我所知,基本上谈及 Haskell 的元编程,必谈 Template Haskell(TH)。我对 TH 的了解属于刚接触,对于进一步了解,需要知晓这样两个概念,抽象语法树(abstract syntax tree,AST),代码语法分析成功以后就会生成 AST,它包含的内容和代码本身是一致的。而 TH 的执行结果,也是生成一棵 AST。

接着要了解的概念是 QuasiQuotation,里面可以存放任何字符串,被视作一个表达式,允许程序员写自定义的结构片段(下面的中括号组合加上里面的竖线的这个结构 [| |])。比如(例子来自这里):

silly :: QuasiQuoter
silly = QuasiQuoter { quoteExp = \_ -> [| "yeah!!!" |] }

这一篇介绍相对比较容易理解(里面还介绍了使用 reify 来自省)。比如 [d|head’ (x:_) = x|] 这样的表达式会被解析成为这样一棵 AST:[FunD head’ [Clause [InfixP (VarP x_1) GHC.Types.: WildP] (NormalB (VarE x_1)) []]]。

如果表达式里面还有 Quotation,就需要使用 $() 来区分,比如:

emptyShow :: Name -> Q [Dec]
emptyShow name = [d|instance Show $(conT name) where show _ = ""|]

无论是上面的哪一个,限制都还是太多了,主要的原因还是在于,它们是静态语言;因此要用元编程用得自如,必须深入学习一门动态语言。

 

来看 JavaScript。从静态语言的囚笼中解脱出来, JavaScript 的元编程的能力虽然强大,但是却很容易归纳:

  • 对对象的自省,对对象方法和属性的改变,这里的对象既包括普通的对象和方法实例,也包括 prototype 这个特殊成员;
  • eval 关键字。

其余那些元编程的特性,都是其他人或者说第三方基于以上元编程基本的能力给后加上去的。

对于第一条,其实可以用下面这个最简单的例子来概括:

function Func(){};
var func = new Func();

func.a = function(){
    console.log("a");
};
Func.b = function(){
    console.log("b");
};
Func.prototype.c = function(){
    console.log("c");
};

// instance
func.a();

// function
Func.b();

// prototype
func.c();

而对于第二条,还是用一个最简单的例子来说明,数据和代码等价的道理(还有一个关于模板引擎使用代码生成的例子在这里):

var str = "{a:3, b:4}";
var obj = eval("(" + str + ")");
console.log(obj);

 

最后是 Groovy,把 Groovy 放在最后是因为它的元编程特性太丰富了(下面的特性,如果要找例子都可以去这个官网的链接)。Java 的所有元编程能力全部保留,在之基础上,下面我有选择地介绍几条。

1、MethodMissing:这是一个我非常喜欢的特性,简言之就是当被调用方法不存在时,可以执行的自定义方法,想一想,这相当于为对象提供了一个重要的特性:default 行为。与 methodMissing 相对的,还有 propertyMissing。

class Foo {
   def methodMissing(String name, def args) {
        return "this is me"
   }
}
assert new Foo().someUnknownMethod(42l) == 'this is me'

2、GroovyInterceptable:这个特性是给方法调用增加一层拦截逻辑,换句话说,是 AOP 的一种实现,比如:

class SimplePOGO implements GroovyInterceptable {
    void targetMethod(){
        System.out.println("...")
    }

    def invokeMethod(String name, args){
        System.out.println("${name} is being called")

        //Get the method that was originally called.
        def calledMethod = SimplePOGO.metaClass.getMetaMethod(name, args)
        calledMethod?.invoke(this, args)
    }
}

simplePogo = new SimplePOGO()
simplePogo.targetMethod()

上面的例子调用 targetMethod,但是拦截逻辑放在 invokeMethod 里面。

3、Categories,这是个从 Objective C 搬过来的特性。这个怎么说呢,很像电脑游戏里面角色的隐藏技能,平时不具备,但是危急关头(使用 use 关键字)可以触发打开,等到危急结束(use 的代码块结束),技能又消失,恢复原状。

use(TimeCategory)  {
    println 1.minute.from.now
    println 10.hours.ago

    def someDate = new Date()
    println someDate - 3.months
}

4、Magic Package。“魔法包”?听起来就很牛的样子,对吧。如果我们遵循 magic package 的命名规约,我们可以创建自定义的元类(MetaClass):

groovy.runtime.metaclass.[package].[class]MetaClass

比如我们要改变 java.lang.String 的逻辑,那就实现一个 MetaClass,并且这个类的路径是:

groovy.runtime.metaclass.java.lang.StringMetaClass

BTW,Groovy 的 MetaClass 的一系列子类能力很强,连 static method 之类的东西都可以改变。更多的元编程特性,去官网找就好了。

但是回过头来看一下,若论功能和特性的种类和纷繁程度,自然没得说,但是从语言设计的简洁性来说,JavaScript 这个老被说 “有缺陷” 的语言却可以甩 Groovy 几条街。这并非一个孰好孰坏的评判,正如同接口的设计一样,有人喜欢最简接口,有人喜欢人本接口。有的语言就是喜欢简洁,像我以前提过的 Io 语言,连关键字都省了;但是像 Perl 呢,却说:

There’s More Than One Way To Do It.

所以,程序员啊,开心最重要了。(-_-)~…

 

在下一篇,会比较一下这四位 DSL 的特性和能力。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

×Scan to share with WeChat

你可能也喜欢看:

  1. Javascript Memoizer
  2. Groovy on Grails 交流活动
  3. JavaScript 3D 图表
  4. 从 Java 和 JavaScript 来学习 Haskell 和 Groovy(类型系统)
  5. java.util.concurrent 并发包诸类概览

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

订阅·联系

四火,啰嗦的程序员一枚,现居西雅图

Amazon Google Groovy Hadoop Haskell Java JavaScript LeetCode Oracle Python Spark 互联网 前端 华为 历史 同步 团队 图解笔记 基础设施 工作 工作流 工具 工程师 应用系统 异步 微博 思考 技术 数据库 曼联 测试 生活 程序员 管理 系统设计 缓存 编码 编程范型 英语 西雅图 设计 评审 问题 面试 项目

分类

  • Algorithm and Data Structure (30)
  • Concurrency and Asynchronization (6)
  • System Architecture and Design (43)
  • Distributed System (18)
  • Tools Frameworks and Libs (13)
  • Storage and Data Access (8)
  • Front-end Development (33)
  • Programming Languages and Paradigms (55)
  • Testing and Quality Assurance (4)
  • Network and Communication (6)
  • Authentication and Authorization (6)
  • Automation and Operation Excellence (13)
  • Big Data and Machine Learning (5)
  • Product Design (7)
  • Hiring and Interviews (14)
  • Project and Team Management (14)
  • Engineering Culture (17)
  • Critical Thinking (25)
  • Career Growth (57)
  • Life Experience and Thoughts (45)

推荐文章

  • 谈谈分布式锁
  • 常见分布式系统设计图解(汇总)
  • 系统设计中的快速估算技巧
  • 从链表存在环的问题说起
  • 技术面试中,什么样的问题才是好问题?
  • 从物理时钟到逻辑时钟
  • 近期面试观摩的一些思考
  • RSA 背后的算法
  • 谈谈 Ops(汇总 + 最终篇):工具和实践
  • 不要让业务牵着鼻子走
  • 倔强的程序员
  • 谈谈微信的信息流
  • 评审的艺术——谈谈现实中的代码评审
  • Blog 安全问题小记
  • 求第 K 个数的问题
  • 一些前端框架的比较(下)——Ember.js 和 React
  • 一些前端框架的比较(上)——GWT、AngularJS 和 Backbone.js
  • 工作流系统的设计
  • Spark 的性能调优
  • “残酷” 的事实
  • 七年工作,几个故事
  • 从 Java 和 JavaScript 来学习 Haskell 和 Groovy(汇总)
  • 一道随机数题目的求解
  • 层次
  • Dynamo 的实现技术和去中心化
  • 也谈谈全栈工程师
  • 多重继承的演变
  • 编程范型:工具的选择
  • GWT 初体验
  • java.util.concurrent 并发包诸类概览
  • 从 DCL 的对象安全发布谈起
  • 不同团队的困惑
  • 不适合 Hadoop 解决的问题
  • 留心那些潜在的系统设计问题
  • 再谈大楼扔鸡蛋的问题
  • 几种华丽无比的开发方式
  • 我眼中的工程师文化
  • 观点的碰撞
  • 谈谈盗版软件问题
  • 对几个软件开发传统观点的质疑和反驳
  • MVC 框架的映射和解耦
  • 编程的未来
  • DAO 的演进
  • 致那些自嘲码农的苦逼程序员
  • Java 多线程发展简史
  • 珍爱生命,远离微博
  • 网站性能优化的三重境界
  • OSCache 框架源码解析
  • “ 你不适合做程序员”
  • 画圆画方的故事

近期评论

  • + 1.943624 BTC.NEXT - https://graph.org/Ticket--58146-05-02?hs=9a9c6f8dfe3cdbe0074006e3e640b19b& on 所有文章
  • Anonymous on 闲聊投资:亲自体验和护城河
  • 四火 on 关于近期求职的近况和思考
  • YC on 关于近期求职的近况和思考
  • mafulong on 常见分布式基础设施系统设计图解(四):分布式工作流系统
  • 四火 on 常见分布式基础设施系统设计图解(八):分布式键值存储系统
  • Anonymous on 我裸辞了
  • https://umlcn.com on 资源链接
  • Anonymous on 我裸辞了
  • Dylan on 我裸辞了
© 2025 四火的唠叨 | Powered by Minimalist Blog WordPress Theme