美文网首页我爱编程
如何评价王垠的《Kotlin和Checked Exception

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

作者: 玩家翁伟 | 来源:发表于2018-06-05 16:04 被阅读373次

    完全赞同王垠对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

        本文链接:https://www.haomeiwen.com/subject/xzxmsftx.html