Pipesを投稿したGabrielです。私は Paolo と協力しており、彼の最初の提案よりもさらに強力でタイプセーフな、より洗練された実装を開発中です。あなたの質問に対する簡単な答えは、最終的な実装は元のパイプのスーパーセットであり、同じ動作とセマンティクスで以前と同じコードを書くことができるということです。
ここでもかなり簡単に要約できます。await ステートメントと yield ステートメントは、パイプが制御を放棄できる唯一の方法であるため、上流または下流のパイプが終了した場合にフォールバックをそれぞれにアタッチします。フォールバックはパイプを永久にダウングレードし、失敗したアクションを繰り返すことはできなくなります。await が失敗するとパイプがプロデューサーにダウングレードされ、yield が失敗するとパイプがコンシューマーにダウングレードされます。生産者が生成に失敗した場合、または消費者が待機に失敗した場合、それらは基本モナドに格下げされ、もはや失敗することはありません。
コンシューマーとプロデューサーは別のタイプになり、公開されなくなりました。これらは、Await または Yield コンストラクターがないことを除いて、型パイプ型と同じです。await ステートメントを禁止できるパイプの入力型がないため、少なくともプロデューサー型ではこれが必要です。
await ステートメントと yield ステートメントは、フォールバック動作としてデフォルトで終了します。これは以前と同じ動作です。await と yield は、それらをサポートするダウングレード状態で動作するように型分類されます。ただし、tryAwait や tryYield よりも魅力的な名前が思いついたら、オプションで独自のフォールバックを提供できるようになりました。
パイプがこの拡張機能でカテゴリを形成していることをまだ確認する必要がありますが、その可能性は非常に高いようです。また、100% タイプセーフであり、型を使用して、ブール値ではなくダウングレードを強制し、プログラマーによって証明された不変式を使用します。
編集:あなたの食欲をそそるいくつかの機能するコード(拡張機能を使用するには、githubリポジトリから「try」ブランチをチェックアウトしてください):
printer = forever $ await >>= lift . print
take' n = replicateM_ n $ await >>= yield
fromList' = mapM_ (yieldOr (lift $ putStrLn "Undelivered elements))
diagnose = forever $ do
x <- awaitOr (lift $ putStrLn "Await failed")
yieldOr (lift $ putStrLn "Yield failed") x
> runPipe $ printer <+< take' 3 <+< diagnose <+< fromList [1..10]
1
2
3
Yield failed
Undelivered elements
> runPipe $ printer <+< take' 10 <+< diagnose <+< fromList [1..3]
1
2
3
Await failed