betterGo

天下苦golang久矣!

今亡亦死,举大计亦死,等死,死国可乎

背景

然而生活中,接收了没有泛型,似乎也挺美好的样子,直到某一天,你发现你又要对slice进行删除操作了,明明你前几天才做过的,明明...

泛型可以不用,但泛型库函数不能没有,我不想再写for了。美好的Map, Reduce,uniq...,你们在哪里?

于是,betterGo诞生了

Implement golang generic by code generating like C++ template (monomorphization)

通过代码生成的方式,像C++ template一样,实现golang泛型函数(术语叫monomorphization)

正如之前文章里说的,我起了个头,验证了可行性后,后续就交给

详情

C++的template实现的泛型,可以简单理解为在编译阶段,识别出其类型,不同的调用都实例化到具体的类型(就是每个泛型库函数都根据调用者的调用参数类型生成一份代码),go自然也可以这样子做。

但,我们没有影响力,不像七牛云一样有知名度(他们自己造了一套语法,转译成golang语法),造语法是不可能的:

IDE 不支持,总不能别人用了这个库,写的代码编译不了,需要调用工具转换后才能跑吧

不可能有人会用的。。

因此,需要暗度陈仓。

先提供interface{}的库函数

golang通过 interface{}可以实现"泛型",但是性能太差,以至于go的作者robpike自己都。。。没眼看。

但没关系,我们可以提供这些函数给用户先使用,在开发,编译,调试都能正常使用,使用如下:

"github.com/PioneerIncubator/betterGo/enum" //引用betterGo的库

func mul(a, b int) (c int) {

c = a * b

return

}

out := enum.Reduce(a, mul, 1).(int)

复制代码

这些测试例子可在项目的test目录中找到。

转译

之后只需要调用一下我们的工具,转换成具体类型的函数即可,自然,我们也会将调用方的函数改变:

out := enum.ReduceAMulInt(a, mul, 1) //这时的enum包就是用户项目自己目录里的包了

复制代码

生成的Reduce函数如下,会在调用方的目录utils/enum/reduce.go里:

package enum

func ReduceAMulInt(argname_1 int, argname_2 func(int, int) int, argname_3 int) int {

lenSlice := len(argname_1)

switch lenSlice {

case 0:

return 0

case 1:

return argname_1[1]

}

out := argname_2(argname_3, argname_1[0])

next := argname_1[1]

for i := 1; i < lenSlice; i++ {

next = argname_1[i]

out = argname_2(out, next)

}

return out

}

复制代码

编译

这时编译就是特例化版本函数的二进制了。

至于生成的代码,可以git checkout .全部扔掉,开发依然使用interface{}版本的代码。

后记

虽然go2 泛型明年就要出了,但也很悬- -,有兴趣可以参与开发这个项目哈,玩玩也行。

支持的函数

find(slice, default \ nil, fun)

Returns the first element for which fun returns a truthy value. If no such element is found, returns default.

map(slice, fun)

Returns a list where each element is the result of invoking fun on each corresponding element of enumerable.

all?(slice, fun \ fn x -> x end)

Returns true if fun.(element) is truthy for all elements in enumerable.

any?(slice, fun \ fn x -> x end)

Returns true if fun.(element) is truthy for at least one element in enumerable.

uniq_by(slice, fun)

Enumerates the enumerable, by removing the elements for which function fun returned duplicate elements.

项目细节:

betterGo实现了我认为Go所缺失的部分

Real Generic

为用户提供了可以直接用在代码中的真正的interface{}。

在部署之前,仅需要使用translator生成确定类型的代码,这种方式并不会影响你的代码性能。

下面是已经实现的所有泛型函数:

enum.Reduce

enum.Map

实现

使用Go AST来分析你使用泛型函数的代码,生成确定类型的函数并替换掉你原先的调用语句

实际上所做的事

背景

现在的Go语言不支持泛型(像C++中的template、Java中的interface)

目前,为实现泛型的需求,在Go语言中往往有如下几种方式1:

Interface (with method)

优点:无需三方库,代码干净而且通用。

缺点:需要一些额外的代码量,以及也许没那么夸张的运行时开销。

Use type assertions

优点:无需三方库,代码干净。

缺点:需要执行类型断言,接口转换的运行时开销,没有编译时类型检查。

Reflection

优点:干净

缺点:相当大的运行时开销,没有编译时类型检查。

Code generation

优点:非常干净的代码(取决工具),编译时类型检查(有些工具甚至允许编写针对通用代码模板的测试),没有运行时开销。

缺点:构建需要第三方工具,如果一个模板为不同的目标类型多次实例化,编译后二进制文件较大。

betterGo就是通过code generation来实现泛型

如何使用

如果你想使用betterGo来通过自动生成代码的方式实现泛型,可以看下面的例子:

在项目中包含了测试用例,例如,需要使用泛型的代码是test/map/map.go,如果想用interface{} 的函数就是enum.Map 这样子用。

如果想生成具体类型的函数,就运行这行命令:go run main.go -w -f test/map/map.go

然后你发现 test/map/map.go 改变了,enum.Map 变成了: enum.MapOriginFn(origin, fn)

然后你看项目目录下生成了: utils/enum/map.go,就是具体类型的函数

参与项目

如果想和我们一起完成项目的开发,可以直接看代码,找到AST相关的包,尝试理解相关函数的作用,很容易就可以理解这个项目以及代码了。

如果想从理论出发的话,可以简单看看这本书:github.com/chai2010/go… ,其实他也就是把AST包里的代码简单讲讲。

想参与具体开发可以参考项目接下来的TODO List

技术思路

导入需要操作的文件/目录

通过AST进行语法分析

AST能分析出每条语句的性质,如:

GenDecl (一般声明):包括import、常量声明、变量声明、类型声明

AssignStmt(赋值语句):包括赋值语句和短的变量声明(a := 1)

FuncDecl(函数声明)

TypeAssertExpr(类型断言)

CallExpr(函数调用语句)

当分析到包含变量的值/类型的语句时(AssignStmt、FuncDecl)会对变量的值和类型进行记录,并建立二者之间的映射关系,以便于在后续环节中能够通过变量名获取变量的类型

当发现函数调用语句(CallExpr)时,会检查该函数是否为我们提供的函数,如果是,则通过上一步中记录的参数名对应的类型生成专门处理该类型的一份代码,并存储到指定路径下(如果之前已经生成过相同类型的代码则不重复生成)

将原代码中的原来的函数调用语句替换成新的函数调用语句,使其调用上一步中新生成的函数,并更新import的包

Reference

[1] Go有什麽泛型的实现方法? - 达的回答 - 知乎

有疑问加站长微信联系(非本文作者)

JAVA泛型特例化_这个大学生,抢先go2实现了go的泛型相关推荐

  1. 这个大学生,抢先go2实现了go的泛型

    betterGo 天下苦golang久矣! 今亡亦死,举大计亦死,等死,死国可乎 背景 然而生活中,接收了没有泛型,似乎也挺美好的样子,直到某一天,你发现你又要对slice进行删除操作了,明明你前几天 ...

  2. java 柯里化_函数式编程(Java描述)——Java中的函数及其柯里化

    本文继续上一篇的内容 在Java中,函数可以表现为一个普通的方法.一个lambda表达式,又或者方法引用,甚至是匿名类.本文不会介绍匿名类这种形式. 方法 Java中的方法,Java使用方法这一概念来 ...

  3. java 科里化_关于柯里化(curry)

    在实际使用的过程中对柯里化有了一些无法从书本上直接获得的感受. 在JAVA中函数不是头等公民,必须通过接口进行外观统一以后,才能通过实例作为载体进行处理逻辑的传递, 最容易理解的例子莫过于Strate ...

  4. java辐射汉化_如何让所有路径(顶点)从Java中的顶点辐射出来?

    dynamodb-titan基于TinkerPop 3.x和TinkerPop 3.x Gremlin不受Groovy的约束 . Groovy中的Gremlin基本上等同于Java . 如果你不学习G ...

  5. java辐射汉化_新研究:低强度环境辐射足以导致量子比特退相干

    美国麻省理工学院和西北太平洋国家实验室(PNNL)的研究人员最近发现,随着量子计算领域的快速发展,量子比特的性能很快就会遇到阻碍.这项研究发表在 8 月 26 日的<自然>杂志上. 研究表 ...

  6. java泛型常用特点_?你必须知道的Java泛型

    前言 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin3923282... 种一棵树最好的时间是十年前,其次是现在 我知道很多人不玩qq了,但是怀旧一下,欢 ...

  7. java泛型 简书_一文带你认识Java泛型基础

    Java泛型基础 1. 认识泛型 泛型是在JDK1.5之后增加的新功能. 泛型可以解决数据的安全性问题, 主要的原理是在类声明的时候通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型. ...

  8. 处理问题的方法--抽象和特例化

    事实上我们在软件开发的过程中总是:遇到问题,解决问题,这么一个 简单的过程.处理一般类似问题的时候,我们经过抽象,有的提取算法,有的提取结构,有的提取流程等等,这样的过程可以简单理解为就是抽象.然而问 ...

  9. Java Review - 并发编程_原子操作类LongAdder LongAccumulator剖析

    文章目录 概述 小Demo 源码分析 重要的方法 long sum() reset sumThenReset longValue() add(long x) longAccumulate(long x ...

最新文章

  1. 多线程的使用——模拟线程池的实现(2015-12-02 00:14:59)
  2. 避免出现anr的方法_ANR原因及解决方法
  3. 一步一步学Ruby(十一):控制语句
  4. MyBatis第3天
  5. TCP/UDP通信解疑
  6. snmp有android代理端吗,GitHub - wosika/SNMP4Android: 简易使用于安卓的SNMP工具类,基于snmp4j...
  7. 驱动人生安装驱动计算机无法启动,驱动人生怎么安装驱动程序?驱动人生基本功能...
  8. 网上赚钱的好方法,实战案例讲解,让你秒懂赚钱的秘密!
  9. [STM32]WIN7 64位系统 CDC类 虚拟串口驱动无法安装的解决办法
  10. 域名泛解析,二级域名转向问题- -完美解决
  11. 基于递归回溯算法实现八皇后游戏问题
  12. GPS定位详解——涉及GPS版本变化、定位获取失败等常见问题。
  13. 信息学奥赛C++语言:蛋糕
  14. 机械转嵌入式开发需要学什么东西?嵌入式软件工程师学习路线
  15. C++: 对称数字金字塔
  16. redis实现阻塞轮训队列
  17. CoreOS发布准生产级Clair1.0
  18. 金蝶云单据下推,转换规则
  19. 首经贸大学计算机专业好吗,首都经贸2+2(首都经贸大学是211吗985吗)
  20. 壳寡糖/肉桂醛修饰乳清蛋白,乳清浓缩蛋白-羟丙基甲基纤维素复合材料

热门文章

  1. Flex 是什么? flex和flash是什么关系?(转)
  2. java事务是基于数据库的么_详解在Spring Boot中使用数据库事务
  3. PLSQL_自治事务和嵌套事物的理解和用法(案例)
  4. 共模电流抑制思路小结
  5. 希希敬敬对Alpha阶段测试报告
  6. Java8 stream特性之一:List转Map方案(返回某个属性或对象本身)
  7. bootstrap设置默认主题皮肤
  8. 众利币开发与模式设计
  9. Python time库、random库概览+Python里面有趣的东西
  10. 硬件和软件看门狗的差别