haskell - 没有错误的健壮的haskell

标签 haskell robustness

我目前正在学习 Haskell。我选择这种语言的动机之一是编写具有高度鲁棒性的软件,即完全定义的、数学确定的、永远不会崩溃或产生错误的函数。我并不是指由系统谓词(“系统内存不足”、“计算机着火”等)引起的故障,这些并不有趣,并且可以简单地使整个过程崩溃。我也不是指由无效声明(pi = 4)引起的错误行为。

相反,我指的是由错误状态引起的错误,我想通过严格的静态类型使这些状态不可表示和不可编译(在某些函数中)来消除这些错误。在我看来,我称这些函数为“纯粹的”,并认为强类型系统可以让我完成这个任务。然而 Haskell 并没有以这种方式定义“纯”,并允许程序通过 error 崩溃。在任何情况下。

Why is catching an exception non-pure, but throwing an exception is pure?

这是完全可以接受的,一点也不奇怪。然而令人失望的是,Haskell 似乎没有提供一些功能来禁止可能导致使用 error 的分支的函数定义。 .

这是一个人为的例子,为什么我觉得这令人失望:

module Main where
import Data.Maybe

data Fruit = Apple | Banana | Orange Int | Peach
    deriving(Show)

readFruit :: String -> Maybe Fruit
readFruit x =
    case x of
         "apple" -> Just Apple
         "banana" -> Just Banana
         "orange" -> Just (Orange 4)
         "peach" -> Just Peach
         _ -> Nothing

showFruit :: Fruit -> String
showFruit Apple = "An apple"
showFruit Banana = "A Banana"
showFruit (Orange x) = show x ++ " oranges"

printFruit :: Maybe Fruit -> String
printFruit x = showFruit $ fromJust x

main :: IO ()
main = do
    line <- getLine
    let fruit = readFruit line
    putStrLn $ printFruit fruit
    main

假设我偏执于纯函数 readFruitprintFruit真的不会因为无人状态而失败。你可以想象代码是用来发射一个装满宇航员的火箭,在一个绝对关键的例程中需要序列化和反序列化水果值。

第一个危险自然是我们在模式匹配中犯了一个错误,因为这给了我们无法处理的可怕错误状态。值得庆幸的是,Haskell 提供了内置的方法来防止这些,我们只需使用 -Wall 编译我们的程序。其中包括 -fwarn-incomplete-patterns和啊哈:
src/Main.hs:17:1: Warning:
    Pattern match(es) are non-exhaustive
    In an equation for ‘showFruit’: Patterns not matched: Peach

我们忘记序列化桃子和showFruit会抛出一个错误。这很容易解决,我们只需添加:
showFruit Peach = "A peach"

该程序现在可以在没有警告的情况下编译,避免了危险!我们发射了火箭,但突然程序崩溃了:
Maybe.fromJust: Nothing

由于以下故障线路,火箭注定要坠入大海:
printFruit x = showFruit $ fromJust x

本质上 fromJust有一个分支,它引发了 Error因此,如果我们从 printFruit 开始尝试使用它,我们甚至不希望程序编译。绝对必须是“ super ”纯净的。我们可以解决这个问题,例如将行替换为:
printFruit x = maybe "Unknown fruit!" (\y -> showFruit y) x

我觉得奇怪的是 Haskell 决定实现严格的类型和不完整的模式检测,这一切都是为了防止无效状态被表示,但由于没有给程序员一种方法来检测到 error 的分支而落到了终点线的前面。当他们不被允许时。从某种意义上说,这使得 Haskell 不如 Java 健壮,后者迫使您声明允许您的函数引发的异常。

最简单的实现方法是简单地 undefined error以某种方式,通过某种形式的关联声明,局部用于函数及其方程使用的任何函数。然而,这似乎是不可能的。

The wiki page about errors vs exceptions为此目的通过契约(Contract)提到了一个名为“扩展静态检查”的扩展,但这只会导致链接断开。

它基本上归结为:我如何让上面的程序无法编译,因为它使用 fromJust ?欢迎所有想法、建议和解决方案。

最佳答案

Haskell 允许任意的一般递归,所以并不是所有的 Haskell 程序都必须是全部的,只要 error以各种形式被删除。也就是说,您可以只定义 error a = error a处理任何你不想处理的情况。运行时错误比无限循环更有帮助。

你不应该想到error类似于普通的 Java 异常。 error是表示编程错误的断言失败,以及类似 fromJust 的函数可以调用error是一个断言。你不应该试图捕捉 error 产生的异常。除非在特殊情况下,例如即使请求的处理程序遇到编程错误也必须继续运行的服务器。

关于haskell - 没有错误的健壮的haskell,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31888246/

相关文章:

android 内容提供商在提供商崩溃时的稳健性

asp.net - Web 应用程序必须支持后退按钮吗?

haskell - 创建我自己的状态 monad 转换器模块,隐藏底层状态 monad

Haskell:模式匹配类型注释中需要ScopedTypeVariables

Python,将整数写入 '.txt'文件

robustness - 稳健性的最佳实践

oop - 子类型优于类型类的优点

haskell - haskell 的出租车号码

Haskell 和函数组合

linux - make 有多健壮?