私は現在 Haskell を学んでいます。私がこの言語を選んだ動機の 1 つは、非常に高度な堅牢性を備えたソフトウェア、つまり、完全に定義され、数学的に決定論的で、クラッシュしたりエラーを生成したりしない関数を作成することでした。システムの述語 (「system out of memory」、「computer on fire」など) によって引き起こされる障害を意味するわけではありません。それらは興味深いものではなく、単にプロセス全体をクラッシュさせる可能性があります。また、無効な宣言 ( ) によって引き起こされるエラーのある動作も意味しませんpi = 4
。
代わりに、厳密な静的型付けを使用して (一部の関数で) それらの状態を表現不能およびコンパイル不能にすることによって削除したい、誤った状態によって引き起こされるエラーを参照します。私は心の中でこれらの関数を「純粋」と呼び、強力な型システムによってこれを達成できると考えました。ただし、Haskell はこのように「純粋」を定義しておらず、プログラムerror
がどのようなコンテキストでもクラッシュすることを許可していません。
例外をキャッチすることは非純粋ですが、例外をスローすることは純粋であるのはなぜですか?
これは完全に受け入れられ、まったく奇妙ではありません。残念なことに、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
readFruit
純粋な関数が実際に機能し、未処理の状態が原因でprintFruit
実際に失敗しないことに私が偏執的であるとしましょう。このコードは、絶対に重要なルーチンで果物の値をシリアル化および非シリアル化する必要がある宇宙飛行士でいっぱいのロケットを打ち上げるためのものであると想像できます。
最初の危険は当然、パターン マッチングでミスを犯したことです。これにより、処理できない恐ろしいエラー状態が発生するからです。ありがたいことに、Haskell はそれらを防ぐための組み込みの方法を提供して -Wall
おり、インクルード-fwarn-incomplete-patterns
と AHAを使用してプログラムをコンパイルするだけです。
src/Main.hs:17:1: Warning:
Pattern match(es) are non-exhaustive
In an equation for ‘showFruit’: Patterns not matched: Peach
Peach フルーツをシリアル化するのを忘れていたためshowFruit
、エラーがスローされていました。これは簡単な修正です。追加するだけです。
showFruit Peach = "A peach"
プログラムは警告なしでコンパイルされるようになり、危険が回避されました! ロケットを発射しますが、突然プログラムが次のようにクラッシュします。
Maybe.fromJust: Nothing
ロケットは運命にあり、次の障害のあるラインが原因で海に衝突します。
printFruit x = showFruit $ fromJust x
基本的fromJust
に、それが発生するブランチを持っているので、絶対に「超」純粋でError
なければならないので、それを使用しようとしてもプログラムをコンパイルしたくありませんでした。printFruit
たとえば、行を次のように置き換えることで修正できます。
printFruit x = maybe "Unknown fruit!" (\y -> showFruit y) x
Haskell が厳密な型付けと不完全なパターン検出を実装することを決定したのは奇妙だと思います。これらはすべて、無効な状態が表現可能になるのを防ぐという壮大な追求の結果であり、プログラマーに分岐を検出する方法をプログラマーに与えないことで、フィニッシュ ラインのすぐ前に落ちerror
ます。禁止されている。ある意味では、これにより Haskell は Java よりも堅牢性が低くなり、関数が発生できる例外を宣言する必要があります。
これを実装する最も簡単な方法はerror
、何らかの形で関連付けられた宣言を介して、関数とその式で使用される関数をローカルで単純に未定義にすることです。ただし、これは可能ではないようです。
エラーと例外に関する wiki ページでは、コントラクトを介してこの目的のために「拡張静的チェック」と呼ばれる拡張機能について言及していますが、リンクが壊れているだけです。
基本的には、次のようになります。上記のプログラムをコンパイルしないようにするにはどうすればよいfromJust
ですか? すべてのアイデア、提案、解決策を歓迎します。