elm的编译与Java检查的异常有何不同?

时间:2017-10-17 22:17:24

标签: java elm checked-exceptions

elmzero-runtime-exceptions的主张是其主要卖点之一(见official website),

但如果你停下来思考它,没有什么可以阻止你除以零或耗尽内存。

elm编译器的基本功能是强制您覆盖可能导致异常的所有可能路径。

例如:

import String exposing (toInt)
toIntOrZero s = case toInt s of
                          Err e -> 0
                          Ok val -> val

但这与java中的infamous“检查例外”功能有什么不同?

public static Integer toIntOrZero(String s) {
    try { return Integer.valueOf(s); }
    catch (NumberFormatException e) { return 0; }
}

我从未听说过java是零运行时异常语言的声明。

2 个答案:

答案 0 :(得分:5)

请不要过于关注什么本质上是营销夸张。 当然有一些错误,你永远无法完全排除任何编译器。

因此,我总是把这些零运行时异常声明带到了一点点,但我想我理解了支持者的意图。 Elm是作为在Javascript中开发前端应用程序的替代方法而创建的,这是一个混乱的世界,异常比比皆是,只是日常生活的一部分。榆树让自己更难以自拔,如果你在应用程序上进行基本的完整性测试,你可能没有太多的努力,你可能赢得曾经有过运行时异常在生产中

榆树在几个方面大大降低了例外的可能性。

  1. 除了Debug.crash之外,语言中没有可抛出异常的概念,顾名思义,它实际上只应用于调试和删除不完整的逻辑路径。
  2. 由于没有可抛出的异常,因此通常通过ResultMaybe等类型来处理问题。

    这可以被认为与Java检查过的异常相似,但在概念上他们感觉与我有很大的不同。让我们面对现实吧。例外被滥用。你在Java中提到了一个例子,其中Integer.valueOf()表示它将返回一个int,但是如果你传递了其他任何东西,它会展开堆栈并冒泡,直到某个函数有希望捕获它。这对我来说非常麻烦,当然,检查异常可以帮助减少失败传播的窗口,但是潜在的事实是异常是业务逻辑的错误工具。

    抛出异常的另一种方法是让类与ResultMaybe Elm类型类似,但在Java的早期几乎不可能完全干净,甚至使用泛型,编写这样的类型比Elm类型的简单性更加繁琐和容易出错。而且由于Elm的封闭式系统,

    1. 非详尽模式匹配导致编译失败
    2. 在Java和Javascript中,没有办法进行详尽的模式匹配检查,因为类型系统不允许它。当然,Typescript已经引入了一些功能,但你必须选择它。在Elm中,您必须明确处理所有情况。当然,我想你可以争辩说,榆树让你选择退出详尽的模式匹配,结束所有的案例陈述与一个全能_,但这只是对该语言的愚蠢滥用。那些支票可以帮到你,而且我觉得我更加安全,因为我没有选择参加榆树的错误检查 - 默认情况下它就在那里!

      1. 不变性
      2. 不变性避免了大量潜在类型的错误,过多地进入这里

        1. Elm Architecture提供了Javascript和Elm之间的清晰分离
        2. Elm编译为Javascript,但是Elm Architecture提供了一个很好的清洁屏障,可以使Javascript的所有讨厌的内容远离Elm编写的纯代码。 Javascript中可能发生的任何异常都应由该障碍处理,这样I / O错误将始终转换为Elm友好的,无异常类型。

          最后,运行时异常仍然是可能的(例如:next tagged Elm question处理由递归的Json解码器定义引起的众所周知的运行时异常),每当我听到某人时我都会畏缩一下说它不可能在榆树中得到例外。问题的实例是异常是可能的,但是在日常的Javascript开发过程中,几乎每个例外都是在Elm中

答案 1 :(得分:4)

正如评论者所指出的,Java有未经检查的异常,因此发生运行时错误 。对于像零除零之类的东西,榆树也有未经检查的例外,但摆脱了在实践中最常见的那些。正如Chad的回答所提到的,Elm的Maybe / Result类型在实践中的工作方式与Java检查的异常完全不同。经验丰富的Elm程序员不会编写像toIntOrZero这样的函数(如果他们这样做了,他们可能不会使用case ... of,而更喜欢像toIntOrZero = String.toInt >> Result.withDefault 0这样的函数。

将多个操作与Result.mapResult.andThen等链接在一起,可以提供一种非常有表现力的方法来处理错误情况,而不会强迫程序员在杂草中过深。例如,如果我们想编写一个通过将ID转换为Int来验证ID的函数,在某些数据结构中查找它(当它可能不存在时),然后验证与该用户关联的某些属性,我们就可以编写像这样的东西:

lookupUser : Int -> Result String User
lookupUser intId = ...

userInGoodStanding : User -> Bool
userInGoodStanding user = ...

isValidId : String -> Bool
isValidId id = 
  String.toInt id 
  |> Result.andThen lookupUser 
  |> Result.map userInGoodStanding
  |> Result.withDefault False

这将读取“将ID转换为int,然后查找关联的用户,然后验证用户,如果有任何失败,则返回False。”你的里程可能会有所不同,但是一旦你习惯了它,我(和许多榆树程序员,我认为!)发现这是一种非常好的编写代码可靠的错误方法。