elm
对zero-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
是零运行时异常语言的声明。
答案 0 :(得分:5)
请不要过于关注什么本质上是营销夸张。 当然有一些错误,你永远无法完全排除任何编译器。
因此,我总是把这些零运行时异常声明带到了一点点,但我想我理解了支持者的意图。 Elm是作为在Javascript中开发前端应用程序的替代方法而创建的,这是一个混乱的世界,异常比比皆是,只是日常生活的一部分。榆树让自己更难以自拔,如果你在应用程序上进行基本的完整性测试,你可能没有太多的努力,你可能赢得曾经有过运行时异常在生产中。
榆树在几个方面大大降低了例外的可能性。
Debug.crash
之外,语言中没有可抛出异常的概念,顾名思义,它实际上只应用于调试和删除不完整的逻辑路径。由于没有可抛出的异常,因此通常通过Result
和Maybe
等类型来处理问题。
这可以被认为与Java检查过的异常相似,但在概念上他们感觉与我有很大的不同。让我们面对现实吧。例外被滥用。你在Java中提到了一个例子,其中Integer.valueOf()
表示它将返回一个int,但是如果你传递了其他任何东西,它会展开堆栈并冒泡,直到某个函数有希望捕获它。这对我来说非常麻烦,当然,检查异常可以帮助减少失败传播的窗口,但是潜在的事实是异常是业务逻辑的错误工具。
抛出异常的另一种方法是让类与Result
和Maybe
Elm类型类似,但在Java的早期几乎不可能完全干净,甚至使用泛型,编写这样的类型比Elm类型的简单性更加繁琐和容易出错。而且由于Elm的封闭式系统,
在Java和Javascript中,没有办法进行详尽的模式匹配检查,因为类型系统不允许它。当然,Typescript已经引入了一些功能,但你必须选择它。在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.map
,Result.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。”你的里程可能会有所不同,但是一旦你习惯了它,我(和许多榆树程序员,我认为!)发现这是一种非常好的编写代码可靠的错误方法。