如何评价王垠的《Kotlin和Checked Exception》?

完全赞同王垠对CE的看法,他真正想推荐的应该是typed racket中的union type,这也非常有启发。

我的看法

在调用一个函数的时候,除了正常的返回值,还很可能需要有响应的各种异常,而异常应该是可以被穷举,并且被确保处理的。

在很多时候,异常的处理流程,也是在程序所预先定义好的流程当中。

函数正常执行,获得预期的返回值,这是主流程;但也可以有分支流程,使用异常来触发分支流程是合理的做法。

CE的存在,确保了函数调用者,必须对函数存在的所有分支流程都进行处理。这对提高程序的健壮性非常有帮助。

举个例子,用户注册。

正常的情况下,用户注册是可以成功的,函数会返回注册成功的用户信息;但是也可能不成功,比方说用户名重复,比方说电话号码重复,比方说会员总数到了等等。

这些都是在事先定义的分支流程。

有CE,由编译器来检查这些分支流程都被一一处理了,我觉得这是很好的事情。

CE提供了一套分支流程务必逐一处理的机制,非常好!

概念

异常错误是两种不同的概念,比方说,程序报OOM error,这就不是事先定义好的分支流程了。

遇到错误,那么进程就直接挂掉好了。

逐层的通过CE去报,一层层 catch然后重新throw,那就完全木有必要了。

这不是CE适用的场景。

什么时候是异常,什么时候是错误,我觉得go语言也对此做出了很好的示范,只不过在go里面,异常被叫做error,而错误被叫做panic。

要明确

明确异常错误的区别,然后对于异常,利用CE,去确保调用方对所有分支流程逐一做显式处理,这是很好的语言特性。

要不要用CE?我的判断标准是相应的异常是否是一个事先定义好的分支流程

是的话,就用;不是的话,就不用;要么直接挂掉,要么吃掉。

返回值

很多RPC方案,都会有返回值错误值的区分。

比方说,thrift Thrift: The Missing Guide。在thrift中,可以这样定义一个远程服务:

 double divide(1:double divisor, 2:double dividend) throws (1:InvalidOperation ouch),

如果除法可以正常计算,那么就返回一个double,如果不能,就抛一个InvalidOperation的异常,并且如果有CE能够确保调用方务必对异常进行处理,是不是挺合理的嘛?

GO的例子

以go为例,上面的thrift接口,实际上是导致以下的代码生成:

type CalculatorDivideResult struct {
    Success *float64            `thrift:"success,0" db:"success" json:"success,omitempty"`
    Ouch    *InvalidOperation `thrift:"ouch,1" db:"ouch" json:"ouch,omitempty"`
}

也就是说,thrift的编译器,会默默的生成一个新的类型,这个类型有两个属性:

一个叫Sucess,类型与函数的正常返回值一致。

一个的命名与类型则与函数的异常一致。

然后,thrift生成出来代码还会有这样:

    if result.Ouch != nil {
        err = result.Ouch
        return
    }
    value = result.GetSuccess()
    return

thrift会默默生成一个『包含正常值+异常值』的隐含类型,远程返回的数据,其实都是这个隐含类型,然后,它会判断这个类型的异常是否有值,如果有值就抛异常,如果没有,就返回正常值

用go来演示其实不够直观,但我一时间没在网上找到别的语言的thrift生成好的代码,上的代码是从 glycerine/golang-thrift-minimal-example 里面搬的,但我做了简化。

不过,重点是:有一个新类型,这个类型别的都没有,就只用来保存函数的返回值 + 异常。

有异常的时候返回异常,不然就返回返回值。

我们再来看王垠提到的typed rocket中union type的定义 4 Types in Typed Racket:

4.4 Union Types

Sometimes a value can be one of several types. To specify this, we can use a union type, written with the type constructor U.

> (let ([a-number 37])
    (if (even? a-number)
        'yes
        'no))
- : Symbol [more precisely: (U 'no 'yes)]
'no
Any number of types can be combined together in a union, and nested unions are flattened.

(U Number String Boolean Char)

a value can be one of several types,这不就是可以用一个类型,包含多个属性,每个属性都是不同类型,然后,我们使用这个类型的时候,永远都使用其中一个属性的值嘛?

在垠神的pysonar2中也可以看到这样的代码:

              if (realType instanceof UnionType) {
                    for (Type t : ((UnionType) realType).types) {
                        if (t instanceof ClassType) {
                            realType = t;
                            break;
                        }
                    }
                }

(垠神把pysonar2的代码从他的github中拿掉啦~但根据他之前开源出来的版本版权写明了是Apache 2.0,我这里这样引用pysonar2的代码应该没有问题吧?)

如果,我们能够在语言层面,支持Union Type,即一个值可能是多种不同类型,但每次都只能是某个具体类型,然后编译器确保我们对各种类型都做出具体的处理,不会遗漏。

这不是很理想嘛?

总结

我一直都知道thrift中生成隐含类型的实现,但是这次看到垠神的博客,才恍然说,原来这可以是Union Type,一下子从只有两个属性的具体实现,上升到了语言理论的层面。

所以我说:完全赞同王垠对CE的看法,他真正想推荐的应该是typed racket中的union type,这也非常有启发。

感谢垠神,15块钱我付了!

挖坑不填

但是他对于Hejlsberg的评价则无法让人苟同,我会认为这体现了王垠自己的思维局限:他没有搞懂这个世界是存在妥协与无奈的。

Hejlsberg在做无奈的妥协而已,王垠却以为Hejlsberg的逻辑“荒谬”。

这个回答就先这样吧`妥协与无奈`部分啥时候等我有空再来写

你可能感兴趣的:(如何评价王垠的《Kotlin和Checked Exception》?)