Error
ケースステートメントの2番目の部分は、最初の部分と一致するはずの-ではないことがすでにわかっているため、再テストを試みるべきではありません。(スキップし/= Error err
ます。)
eval e1 = v1
最初に行った評価をやり直そうとします。あなたはそれをする必要はありません。
これがあなたが意図したことだと思います:
eval :: Exp -> Error Val
eval (App e1 e2) = case eval e1 of
Error _ -> Error "Not an application"
S v1 -> case eval e2 of -- nested case statement
Error _ -> Error "Not an application"
S v2 -> appVals v1 v2 -- match the S away
しかし、それはすべて少し醜いように思われるので、Gabriel Gonzalezから優れたアドバイスを受けて、からアプリケーションを作成しましょうError
。
instance Functor Error where
fmap f (Error e) = Error e -- pass through errors
fmap f (S x) = S (f x) -- edit successes
たとえばfmap (+4) (Error "oops") = Error "oops"
、fmap (+4) (S 5) = S 9
。
これfmap
がまったく新しい場合は、Functorsチュートリアルを読んでみませんか?
次に、Applicativeインスタンスを作成しましょう。Applicativeを使用すると、単純な関数のような複雑な関数を使用できます。import Control.Applicative
ファイルを機能させるには、ファイルの先頭に配置する必要があります。
instance Applicative Error where
pure x = S x -- how to put ordinary data in
S f <*> S x = S (f x)
Error e <*> _ = Error e
_ <*> Error e = Error e
ここで、エラーがなかった場合は、次のように定義します。
appVal' :: Val -> Val -> Val
eval' :: Exp -> Val
eval' (App e1 e2) = appVal' (eval' e1) (eval' e2)
アプリケーションを使用すると、で定義した配管を実行することを除いて<$>
、少し同じように機能するものを使用できます。同様に、追加の配管を除いて、関数適用と少し似ています。$
fmap
<*>
eval :: Exp -> Error Val
eval (App e1 e2) = appVals <$> eval e1 <*> eval e2
これは、機能に焦点を合わせながら、舞台裏でエラーを処理するための優れたクリーンな方法です。