実際には2つの解決策があります。
最初の解決策は、Daniel Wagnerが提案したものです。同じLeft
タイプを使用するように、2つのベースモナドを変更します。たとえば、両方を使用するように正規化できますByteString
。これを行うには、最初にByteString
のpack
関数を使用します。
pack :: String -> ByteString
EitherT
次に、それを持ち上げて、 :の左側の値を処理します。
import Control.Error (fmapLT) -- from the 'errors' package
fmapLT pack :: (Monad m) => EitherT String m r -> EitherT ByteString m r
次に、以下を使用して、その変換をConsumer
ベースモナドにターゲティングする必要がありhoist
ます。
hoist (fmapLT pack)
:: (Monad m, Proxy p)
=> Consumer p a (EitherT String m) r -> Consumer p a (EitherT ByteString m) r
同じベースモナドを持っているので、プロデューサーと直接コンシューマーを構成できるようになりました。
2番目の解決策は、DanielDiazCarreteが提案したものです。EitherT
代わりに、2つのパイプで、両方のレイヤーを含む共通のモナド変換子スタックについて合意します。あなたがしなければならないのは、これらの2つのレイヤーをネストする順序を決定することだけです。
EitherT String
変圧器の外側に変圧器を重ねることを選択したと想像してみましょうEitherT ByteString
。つまり、最終的なターゲットモナド変換子スタックは次のようになります。
(Proxy p) => Session (EitherT String (EitherT ByteString p)) IO r
次に、その変圧器スタックをターゲットにするために、両方のパイプをプロモートする必要があります。
あなたの場合、その間にレイヤーConsumer
を挿入する必要があり、その最終的なモナド変換子スタックと一致させたい場合。レイヤーの作成は簡単です。を使用するだけですが、これら2つの特定のレイヤーの間のリフトをターゲットにする必要があるため、プロキシモナド変換子とモナド変換子の両方をスキップする必要があるため、を2回使用します。EitherT ByteString
EitherT String
IO
lift
hoist
EitherT String
hoist (hoist lift) . consumer
:: Proxy p => () -> Consumer p a (EitherT String (EitherT ByteString IO)) ()
最終的なモナド変換子スタックを一致させたい場合は、プロキシモナド変換子と変換子の間にレイヤーProducer
を挿入する必要があります。繰り返しになりますが、レイヤーの作成は簡単です。を使用するだけですが、これら2つの特定のレイヤーの間のリフトをターゲットにする必要があります。あなたはただ、しかし今回は一度だけそれを使用します、なぜならあなたは正しい場所に寄り添うためにプロキシモナド変換子をスキップする必要があるだけだからです:EitherT String
EitherT ByteString
lift
hoist
lift
hoist lift . producer
:: Proxy p => () -> Producer p a (EitherT String (EitherT ByteString IO)) r
これで、プロデューサーとコンシューマーは同じモナド変換子スタックを持ち、それらを直接構成できます。
さて、あなたは疑問に思うかもしれません:このhoist
inglift
のプロセスは「正しいこと」をしているのでしょうか?答えはイエスです。圏論の魔法の一部は、を使用して「空のモナド変換子層」を正しく挿入することの意味をlift
厳密に定義できることです。同様に、hoist
いくつかを指定することにより、 「2つのモナド変換子の間にあるものをターゲットにする」ことの意味を厳密に定義できます。理論的に着想を得た法律とそれを検証しlift
、hoist
それらの法律を遵守します。
lift
これらの法則を満たせば、正確に何をするかについての本質的な詳細をすべて無視することができhoist
ます。圏論は、モナド変換子の間に空間的に「リフトを挿入する」という観点から考える非常に高い抽象化レベルで作業することを可能にし、コードは私たちの空間的直感を厳密に正しい動作に魔法のように変換します。
EitherT
単一のレイヤーでプロデューサーとコンシューマーの間でエラー処理を共有できるため、おそらく最初のソリューションが必要になると思います。