conduitなどで使われているポピュラーな方法は、以下のようにIterateeのコンストラクタを追加し、値を戻す挙動を表現するものです。
haskell
1data Iteratee i o = Done o | Next (i -> Iteratee i o) | Leftover i (Iteratee i o)
2
3peek :: Iteratee i i
4peek = Next $ \i -> Leftover i (Done i)
しかし、継続モナドを使えば、Iterateeの定義を変更せずに値を戻すこともできます。ContTでもいいですがここではkan-extensionsのCodensityを使います。
haskell
1newtype Codensity m a = Codensity
2 { runCodensity :: forall b. (a -> m b) -> m b
3 }
4
5peek :: Codensity (Iteratee i) i
6peek = Codensity $ \cont -> Next $ \i -> case cont i of
7 Next k -> k i
8 Done o -> Done o
contにiを渡すと、peekの次に実行されるIterateeが返ってきます。それにiを再び押し込めばよいというわけです。しかし、次がDoneの場合は値が捨てられてしまうので、そこを気にする場合はDoneにフィールドを追加して余った値を残せるようにするとよいでしょう。
Iterateeのような構造をモナドにする際、>>=の計算量が深さに比例するという難点がありますが、Codensityモナドならばデータ構造を走査する必要がないため、パフォーマンスの面でも有利です。この技法はConduitTの定義でも利用されています。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。