简介

(低能预警:这是一篇毫无技术内容的日常)

ezorm https://github.com/ezbuy/ezorm是我司内部的orm工具,日常用来生成mongo、mysql、sql server框架代码,省了很多事情。其中我用的最多的就是mongo代码的生成。这个工具的核心,一个是生成原理就是渲染模板文件,一个关于驱动,就是万年好死不死的mgo(连mongo官方工具集用go写的那套都是这个库,也没什么好办法)。

顺便吐槽一下,我之前也做过类似将mgo的常用代码规范化的事情,但是为啥不做orm工具咧,因为上一家公司的项目内部,有精确规划mongo连接的需求,需要对mgo的使用定制化,比如几条连接到主库,几条连接到从库等等,普适框架无法概括。但是做电商的业务,mongo没有位于热点数据的话,不会有这个需求。

ezorm的日常使用

ezorm生成的代码日常使用是这样的


users , err := ezorm.UserMgr.find(query)

user, err := ezorm.UserMgr.findOneBy{unique index}(unique index)


这样的代码,正常人都会这么写,其实恶心的还是golang

因为项目中代码,日常是这样的

users , err := ezorm.UserMgr.find(query)

for _, user := range users {
	retArry = append(retArray, doUser(user))
	...
}

for … range 写多了,不管你无不无聊,反正我觉得我想吐

golang没有泛型,但是有代码生成就可以

for … range 写吐这个想法,当然不止我有,翁伟同学(ezbuy CTO)早就有了,所以他对常用类型搞了一个库去支持消除for循环的append(库忘了在哪,反正我赶脚没啥用)。因为golang不支持泛型,所有用处有限。但是orm就可以嘛,反正你的代码是生成的,跟编译期模板展开也没啥区别。

比如这样ForEach,例子是转换int64数组到int32数组

一般golang程序员:


var i64Array []int64
var i32Array []int32
for _, i64 := range {
	i32Array = append(i32Array, int32(i64))
}

这个代码也没什么可说的。

吐过的golang程序员

type []int64 I64ArrayIter

func (this *I64ArrayIter) ForEach(f func (int,int64)) {
	for i, i64 := range this {
		f(i, i64)
	}
}

var i64Array []int64
var i32Array []int32

iter := I64ArrayIter(i64Array)

iter.ForEach(func (i int, i64 int64){
	i32Array = append(i32Array, int32(i64))
})

(是不是看上去酷炫了一点!)

(老师,还有更给力的吗?)

(有!老师学过FP!)

吐出过屎的golang程序员

type []int64 I64ArrayIter

func (this I64ArrayIter) ForEach(f func (int,int64)) {
	for i, i64 := range this {
		f(i, i64)
	}
}

type []int32 I32ArrayIter
func (this I32ArrayIter) Append(i32 int32) I32ArrayIter{
	this = append(this, i32)
}

var i64Array []int64
var i32Array []int32

iter64 := I64ArrayIter(i64Array)
iter32 := I32ArrayIter(i32Array)

iter.ForEach(func (i int, i64 int64){
	iter32.Append(int32(i64))
})

(这货是伪代码,不要跑;这货是伪代码,不要跑;这货是伪代码,不要跑)

(因为这样append是没有用的,我只是懒得写了)

(但是讲道理这样的代码是不是酷炫了很多捏?)

其实这样的写法,就是泛型展开而已,来看上面的抽象模板

type []{Type} {Type}ArrayIter

func (this {Type}ArrayIter) ForEach(f func (int, {Type})) {
	for i, one := range {Type}ArrayIter {
		f(i, one)
	}
}

func (this {Type}ArrayIter) Append(data {Type}) {Type}ArrayIter {
	{Type}ArrayIter = append({Type}ArrayIter, data)
	return {Type}ArrayIter
}

同理,这只是示意,这样append是没有任何卵用的。

那么皮一下ezorm

我在我的ezorm fork中加入了这么一个模板文件

内容就不贴了。

其中,能让append生效的定义是

type {.Name}Iter struct {
   data []*{.Name}
}

实现了我觉得比较有用的ForEach, Append,Filter,Len等FP常用的函数

生成的代码大约可以做到如下鬼畜:

blogs := []*Blog{
		BlogMgr.NewBlog(),
		BlogMgr.NewBlog(),
		BlogMgr.NewBlog(),
		BlogMgr.NewBlog(),
	}

	iter := NewBlogIter(blogs)
	
	filIndex := func(i int) bool { return i%2 == 0 }
	fileId := func(b *Blog) bool { return b.ID.Counter()%2 == 0 }
	foreach := func(i int, b *Blog) {}

	length := iter.Append(BlogMgr.NewBlog()).Foreach(foreach).
		Append(BlogMgr.NewBlog()).Foreach(foreach).
		Append(BlogMgr.NewBlog()).Foreach(foreach).
		Append(BlogMgr.NewBlog()).Foreach(foreach).
		Append(BlogMgr.NewBlog()).Foreach(foreach).
		FilterByIndex(filIndex).Foreach(foreach).
		Filter(fileId).Foreach(foreach).
		Len()

	t.Log(length)

(猫头妙啊.jpg)

用例文件在这里

总结

一直想给golang做一个支持泛型的脚本语言,但是由于各种原因(主要是不会)没做。然后发现在基于模板的代码生成工具里面,做到一定程序的泛型其实很容易哟。咻咪!(逃)。

当然这个patch,我是不会尝试合并到主分支的。个人趣味而已,对工具的价值没有任何用处。