4

私はhaskellでエラーケースを処理する方法をよりよく理解しようとしており、それを支援するためにいくつかのコードを作成しました。

複数の選択肢(ネストされた大文字小文字の表現など)を処理するためのより良い方法(よりエレガントで、より短く、より一般的)はありますか?トピックに関する素晴らしいチュートリアルはありますか?

この例のメイクアップタイプ。ほとんどの場合、これらのネストされたタイプだけでなく、順次取得できる依存値があるため、これは少し単純化されています(たとえば、stdinからIDを読み取り、データベースからこのIDのレコードを取得します)。したがって、ここでのネストは、外部値がすでにチェックされている場合にのみ内部値が使用可能になる場合を示す必要がありますNothing。より良い例については、私の新しい質問を参照してください。

type MyType = (Maybe (Maybe Int))

目標

10未満の場合はintを返し、それ以外の場合(10以上、NothingまたはJust Nothing)は異なるエラーメッセージを返します。

process Nothing ~> "error"
process (Just Nothing) ~> "error2"
process (Just (Just 20)) ~> "error3"
process (Just (Just 5)) ~> "5"

これまでに試しました:

ナイーブな実装。

「忍び寄るくぼみ」に苦しむ

process :: MyType -> String
process t = case t of
        Nothing -> "error"
        Just a -> case a of
                    Nothing -> "error2"
                    Just b -> if b < 10 then show b else "error3"

多分機能

多分関数を使用すると、短くなりますが、読みにくくなります。

process2 :: MyType -> String
process2 t = maybe "error" (\a -> maybe "error2" (\b -> if b < 10 then show b else "error3") a) t

パターンマッチング

これまでで最も優れたソリューションですが、より複雑なケースでは不可能です(MyTypeのタイプ定義の上のコメントを参照してください)。

process3 :: MyType -> String
process3 Nothing = "error"
process3 (Just Nothing) = "error2"
process3 (Just (Just a))
  | a < 10 = show a
  | otherwise = "error3"

コードの要点はhttps://gist.github.com/4024395にあります。

4

2 に答える 2

6

ネストMaybesは確かに厄介です。

提案1:カスタムエラータイプをロールして使用するEither

data MyError = ReadError | TooBig Int

explain :: MyError -> String
explain ReadError = "Error: the requested Int could not be found"
explain TooBig i = "Error: the supplied Int should be at most 10, but it was " ++ show i

次に、Eitherを使用してOk値(右)とエラー値(左)を混合します。

type MyType = Either MyError Int

のような便利な関数がたくさんeitherあり、ApplicativeインスタンスとMonadインスタンスをEither a使用すると、優れたコードを簡単に記述できます。

myAdd :: MyType -> MyType -> MyType
myAdd i1 i2 = (+) <$> i1 <*> i2

うまく適用できる、または

myMult i1 i2 = do
    a <- i1
    b <- i2
    return $ a * b

モナディック表記を好む場合。eitherプログラムをクラッシュさせる方法で使用できます

myShow :: MyType -> String
myShow = either (error.explain) show 

またはとにかく教えてください:

process4 :: MyType -> String
process4 = either explain show

提案2:カスタムタイプをロールする

data MyType' = OK Int | ReadError | TooBig Int

パターンマッチングを使用します。高階関数の再利用が失われるため、これは私のveiwの提案1ほど良くはありませんが、Maybe (Maybe Int)

提案3:エラーモナドを使用する

Control.Monad.Errorについて読み、提供されている関数またはErrorTモナド変換子を使用します。

于 2012-11-06T13:28:45.107 に答える
1

最も読みやすい方法は、ラムダ(ポイントフリースタイル)を避け、最も内側のチェック(に置き換える)にも使用することでmaybe、すでに試した関数です。maybeifmfilter

import Control.Monad(mfilter)

process2 :: MyType -> String
process2 =  
  maybe "error"  $ 
  maybe "error2" $ 
  maybe "error3" 
  show . mfilter (<10) . Just 
于 2012-11-06T22:45:33.953 に答える