Scala で Iteratee と Enumerator を書く練習

とりあえず使い方だけメモ

package com.github.cooldaemon.scalaz_test

import scalaz._

object TryIteratee {
  import Scalaz._
  import IterV._

  def run {
    implicit val ListEnum = new Enumerator[Seq] {
      def apply[E, A](e: Seq[E], i: IterV[E, A]): IterV[E, A] = {
        e match {
          case Seq() => i
          case x::xs => i.fold(
            done = (_, _) => i,
            cont = k => apply(xs, k(El(x)))
          )
        }
      }
    }

    implicit val StreamEnum = new Enumerator[Stream] {
      def apply[E, A](e: Stream[E], i: IterV[E, A]): IterV[E, A] = {
        e match {
          case Stream() => i
          case x #:: xs => i.fold(
            done = (_, _) => i,
            cont = k => apply(xs, k(El(x)))
          )
        }
      }
    }

    val seq = Seq.range(1, 5)
    
    head(seq).run assert_=== Some(1)
    
    val m = for {
      x <- head[Int]
      y <- head[Int]
    } yield (x, y)

    m(seq).run assert_=== (Some(1), Some(2))

    def dropAndPrintAll[E] :IterV[E, Unit] = {
      def step(s: Input[E]) :IterV[E, Unit] = {
        s(
          el    = {e =>
            println(e)
            Cont(step)
          },
          empty = Cont(step),
          eof   = Done((), EOF[E])
        )
      }
      Cont(step)
    }
    println("--<自作 Iteratee>--")
    dropAndPrintAll(seq).run
    
    def peekAndPrintAll[E] :IterV[E, Unit] = {
      def step(initS: Input[E])(s: Input[E]): IterV[E, Unit] = {
        s(
          el    = {e =>
            println(e)
            Cont(step(initS))
          },
          empty = Cont(step(initS)),
          eof   = Done((), initS)
        )
      }
      Cont({s => step(s)(s)})
    }

    val m2 = for {
      _ <- peekAndPrintAll[Int]
      x <- head[Int]
    } yield x

    println("--<Iteratee の合成>--")
    m2(seq).run assert_=== Some(1)
    
    println("--<Enumerator を後から追加>--")
    val m3 = dropAndPrintAll(seq)
    val m4 = m3(Stream.range(5, 10)) // Seq に Stream を追加しても良い
    m4.run
  }
}

解説は後で。例とは言え Iteratee の中に副作用が存在する所がダサい。

疑問点その1

下記を末尾再帰にするには…

      def apply[E, A](e: Seq[E], i: IterV[E, A]): IterV[E, A] = {
        e match {
          case Seq() => i
          case x::xs => i.fold(
            done = (_, _) => i,
            cont = k => apply(xs, k(El(x)))
          )
        }
      }

下記のようにする必要がある。何でだろー?

      @annotation.tailrec def apply[E, A](e: Seq[E], i: IterV[E, A]): IterV[E, A] = {
        val next: Option[(Seq[E], IterV[E, A])] = e match {
          case Seq() => None
          case x::xs => i.fold(
            done = (_, _) => None,
            cont = k => some((xs, k(El(x))))
          )
        }
        next match {
          case None => i
          case Some((es, is)) => apply(es, is)
        }
      }

疑問点その2

Haskell みたいに Enumerator 同士を合成する方法が解らない