2016-04-17 4 views
3

さまざまなエラータイプを持つExceptタイプ(transformersパッケージ)を返す関数がいくつかありますが、すべてShowを実装しています。エラーの場合は、メッセージを印刷したいだけです。一般的なエラータイプクラスによってさまざまなタイプのエラーを含む "例外"

どのような方法で作成/バインドするのですか?

は私が前にすべてのfun*ため、エラーの種類を変換する、またはすべてのshowable種類を取り、String S(RankNTypes?)に変換ラッパーを書き、それは醜いと感じることができました。型システムを使って、自動的にすべてを処理する、より良い方法がありますか?

例コード:

module Main where 

import   Control.Monad.Trans.Except 
import qualified Data.Text as T 

type StrError = String 
type TxtError = T.Text 

funA :: String -> Except StrError String 
funA = return 

funB :: String -> Except TxtError T.Text 
funB = return . T.pack 

fun :: String -> IO() 
fun s = case runExcept (funB =<< funA s) of 
      Left e -> putStrLn $ show e 
      Right _ -> return() 

main :: IO() 
main = fun "foo" 
+0

[Control.Exception](https://hackage.haskell.org/package/base-4.8.2.0/docs/Control-Exception.html)あなたが探しているもののようですSomeExceptionタイプを持っていますfor ... – Alec

+0

@Alec:あなたのエラータイプを 'Exception'クラスのインスタンスにして' SomeException'を使うことをお勧めしますか? (名前にもかかわらず、例外は例外とは関係ありません)。うまくいく解決策のように聞こえる、私は本当にそれが良いかどうかは分からない。 –

+0

さらに詳しく調べると、私はそれがまったく役に立たないと思います。 'Exception'クラスでは、' displayException :: e - > String'を実装する必要があります。 – Alec

答えて

1

最も簡単な方法は、新しいExceptionタイプを作成することです(これは-XExistentialQuantificationを必要としない)表示することができる例外の。

data Exception = forall e . Show e => Exception e 

次に、コードをコンパイルするために変更する必要があるのはすべてタイプシグネチャです。

funA :: String -> Except Exception String 
funB :: String -> Except Exception T.Text 

また、いつでもあなたは今Exceptionコンストラクタでそれをラップする必要があります(上記のコードには例がないうち)例外を作成します。

throwsA :: String -> Except Exception String 
throwsA = throwE . Exception 

throwsB :: T.Text -> Except Exception T.Text 
throwsB = throwE . Exception 

EDIT私はControl.ExceptionからExceptionを導出その後、(適切なShowで)あなたの例外のための新しいタイプを作成することをお勧めします。これはオーバーヘッドですが、異種の例外タイプ間を行き来する必要がある場合は、後で役立ちます。これが私のやり方です。

{-# LANGUAGE DeriveDataTypeable #-} 
module Main where 

import Data.Typeable 
import Control.Exception 
import Control.Monad.Trans.Except 
import qualified Data.Text as T 
import Control.Monad 

newtype StrError = StrError String deriving (Show,Typeable) 
newtype TxtError = TxtError T.Text deriving (Show,Typeable) 

instance Exception StrError 
instance Exception TxtError 

toErr :: Exception e => Except e a -> Except SomeException a 
toErr = mapExcept (either (Left . toException) Right) 


funA :: String -> Except StrError String 
funA = return 

funB :: String -> Except TxtError T.Text 
funB = return . T.pack 

throwsA :: String -> Except StrError String 
throwsA = throwE . StrError 

throwsB :: T.Text -> Except TxtError T.Text 
throwsB = throwE . TxtError 


fun :: String -> IO() 
fun s = case runExcept $ (toErr . funA >=> toErr . funB) s of 
      Left e -> putStrLn $ displayException e 
      Right _ -> return() 

main :: IO() 
main = fun "foo" 
+0

明日はそれを詳細に説明しますが、これまでに見たことから、自分のエラー/例外の実在型(Show制約付き)を書く方が良いでしょう。エラーを 'Control.Exception'のインスタンスにすることは、スローされるべきではないので誤ったものになります。さらに、インスタンス宣言の数を減らすことができます。もし私が間違っていなければ、このアプローチは 'Show e => Except e'ですぐに動作します。 –

+1

例外をスローしないと確信できるなら、それは確かにおそらくより良いアプローチです。 – Alec

0

withExceptT T.unpack . funB =<< funA s。あなたのショーは余計です。 Control.Lensを使用して

fun = void . _Left putStrLn . runExcept . (withExceptT T.unpack . funB <=< funA)

+0

これはまさに私が尋ねたものではありません。私はあなたのアプローチについて言及しました: "私はすべての楽しい* [...]のエラータイプを変換することができます - 問題は:これは、さまざまな種類のエラーを伴う呼び出しがある場合には定型コードです。 –

関連する問題