6

ラップされたタイプを変更できることを知っているので、

f :: (a -> m b)
g :: (b -> m c)
f >>= g :: (a -> m c)

しかし、変更することは可能mですか? mとが と の両方MonadErrorで実装されている場合、どういうわけかそれらをチェーンできますか? 明らかに、それらを直接チェーンすることはできません。ただ、どちらの場合でも電話してしまう状況にあるのですが、それ以上の解決策が見つかりません。Either ErrorAEither ErrorBLeftshow

case mightFail1 of
  Left e -> show e
  Right v -> either show doStuff mightFail2

明示的にチェックしなくても、最初のエラーで停止するというモナドの動作を適切に使用できません。

4

4 に答える 4

11

「コンテナを変更する」という概念全体を「自然な変換」と呼びます。具体的には、コンテナの内容に影響を与えずにコンテナを変換する関数が必要です。を使用して、型システムでこれが当てはまることを確認できforallます。

-- natural transformation
type (m :~> n) = forall a. m a -> n a

その後、これらはいつでも適用できます。たとえば、変身できればErrorA -> ErrorB一般的な操作があります。

mapE :: (e -> e') -> (Either e :~> Either e')
mapE f (Left e)  = Left (f e)
mapE _ (Right a) = a

型演算子と合計型を使用することもできます。

-- a generic sum type
infixr 9 :+:
newtype (a :+: b) = Inl a | Inr b

liftE :: (Either e :~> Either (e' :+: e))
liftE = mapE Inr

バイファンクターはほぼ同じ効果を達成しますが、問題の見方がまったく異なります。通常、コンテナーを変更する代わりに、コンテナー自体の別の共変パラメーター (またはインデックス) に影響を与えます。そのため、Bifunctor アクションは常に自然な変換と見なすことができますが、NT はより一般的です。

于 2013-08-05T22:21:16.150 に答える
7

モナドチェーンでこれを行うことはできません。

これはまったく関係ないことに注意してくださいMonad: ネストされたモナド アクションを引数などにバインドすることはまったくありませんがLeft、引数自体を変換しているだけです。これは基本的にファンクター操作fmapですが、右部分ではなく左部分にあります。

fmap     :: (r->ρ) -> Either l r -> Either l ρ
fmapLeft :: (l->λ) -> Either l r -> Either λ r

その特定のシグネチャを持つ関数は、驚くべきことに、存在しないようです。ただし、2 つの共変引数を持つファンクターのこのアイデアは、単なる よりも明らかに一般的でEitherあり、実際には専用の class があります。それは持っています(IMOはむしろ不幸な命名、と衝突しArrowます)

Data.Bifunctor.first :: (a -> b) -> p a c -> p b c

実際に専門とする

first :: (a -> b) -> Either a c -> Either b c

だからあなたが使うことができます

f :: (Show a) => (Either a b) -> (Either String b)
f = first show
于 2013-08-05T20:33:57.890 に答える
4

この特定のケースではfmapL、私のerrorsライブラリから使用できます:

fmapL :: (a -> b) -> Either a r -> Either b r

最終的に両方に行くと言ったのでshow、 を使用して両方を統合してモナドfmapL showに同意し、それらを直接シーケンスすることができます。Either String

do v <- fmapL show mightFail1
   fmapL show $ mightFail2 v

これで、do 表記を使用して両方を順序付けし、同じエラー処理メカニズムを共有させることができます。

show左の値を統一する唯一の方法ではないことに注意してください。... を使用して、表示できない値を統合することもできますEither

example :: Either (Either Error1 Error2) ()
example = do
    v <- fmapL Left mightFail1
    fmapL Right $ mightFail2 v
于 2013-08-06T00:44:53.817 に答える
2

StackOverflow は優れたラバー ダックです。解決策を見つけましが、別のアプローチがあるかどうかはまだ知りません。質問を書き終えたので、とにかく投稿します。

「連鎖中にモナド型を変更する」ことを考える代わりに、連鎖する前にすべての値を単純に変換して、それらを return にしますEither String a:

f :: (Show a) => (Either a b) -> (Either String b)
f = either (Left . show) (Right)

これにより、いずれかへの呼び出し全体がラップされます ( f mightFail1)。構成可能なバリアントが存在する可能性があります ( f . mightFail1)

クレイジーモード: いずれかを newtype にラップし、関数を右側ではなく左側にマップする functor のインスタンスにし、fmap show mightFail1 を呼び出すだけです (newtype のラップとアンラップを忘れないでください)。それは理にかなっていますか?:D

于 2013-08-05T19:36:38.387 に答える