Skip to content

Commit

Permalink
#53 - safety checkin to show that combining enumerators into iterator…
Browse files Browse the repository at this point in the history
…s seems to stop iterators being re-used
  • Loading branch information
chris-twiner committed Jan 8, 2024
1 parent 05204b2 commit 7c59489
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 131 deletions.
41 changes: 16 additions & 25 deletions scales-xml/src/main/scala/scales/utils/iteratee/Iteratees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package scales.utils.iteratee

import scalaz.{Applicative, EphemeralStream, Monad}
import scalaz.iteratee.Input.{Element, Empty, Eof}
import scalaz.iteratee.Iteratee.{cont, done, empty, foldM, iterateeT}
import scalaz.iteratee.Iteratee.{cont, done, elInput, empty, enumEofT, foldM, iterateeT}
import scalaz.iteratee.StepT.{Cont, Done}
import scalaz.iteratee.{EnumeratorT, Input, IterateeT, StepT}
import scales.utils.ScalesUtils
Expand All @@ -18,10 +18,10 @@ trait Eval[WHAT, F[_],RETURN] {

def eval(implicit F: Monad[F]) : IterateeT[WHAT, F, RETURN] =
iterateeT(
F.bind((orig &= empty[WHAT, F]).value)((s: StepT[WHAT, F, RETURN]) => s.fold(
F.bind((orig &= enumEofT[WHAT,F]).value)((s: StepT[WHAT, F, RETURN]) => s.fold(
cont = k => k(Eof[WHAT]).value
, done = (a, i) => F.point(Done(a,i))
)))
, done = (a, i) => F.point(Done(a, i))
)))
}

trait IterateeImplicits {
Expand All @@ -44,20 +44,18 @@ trait IterateeImplicits {
* s => s.mapContOr(_ => sys.error("diverging iteratee"), apply(s))
* }
* }
* } */

// def enumIterator[E, F[_]](x: => Iterator[E])(implicit F: MonadIO[F]) : EnumeratorT[E, F] =
def iteratorEnumerator[E, F[_]](iter: => Iterator[E])(implicit f: Monad[F]): EnumeratorT[E, F] =
* }
*/
def iteratorEnumerator[E, F[_]](iter: Iterator[E])(implicit f: Monad[F]): EnumeratorT[E, F] =
new EnumeratorT[E, F] {
def apply[A] = {
def go(xs: Iterator[E])(s: StepT[E, F, A]): IterateeT[E, F, A] =
if (xs.isEmpty) s.pointI
else {
else
s mapCont { k =>
val next = xs.next()
k(Element(next)) >>== go(xs)
k(elInput(next)) >>== go(xs)
}
}

go(iter)
}
Expand Down Expand Up @@ -249,20 +247,13 @@ trait Iteratees {
*/
def foldIM[E,F[_],A]( f : (E,A) => F[A] )( init : A )(implicit F: Monad[F]) : ResumableIter[E,F,A] = {
def step( current : A )( s : Input[E] ) : IterateeT[E,F,(A, IterateeT[E,F,_])] =
iterateeT[E,F,(A, IterateeT[E,F,_])](
s(
el = e => {
F.bind(F.point(e)) { e =>
val next = f(e, current)
F.map(next) {
i =>
Done[E, F, (A, IterateeT[E, F, _])]((i, iterateeT(F.point(Cont(step(i))))), Empty[E])
}
}
s(
el = e =>
IterateeT.IterateeTMonadTrans[E].liftM(f(e, current)) flatMap{ i =>
done[E, F, (A, IterateeT[E, F, _])]((i, iterateeT(F.point(Cont(step(i))))), Empty[E])
},
empty = F.point( Cont(step(current)) ),
eof = F.point( Done((current, iterateeT( F.point( Cont(step(init))))),Eof[E]) )
)
empty = cont(step(current)),
eof = done((current, iterateeT( F.point( Cont(step(init))))),Eof[E])
)

iterateeT( F.point( Cont(step(init)) ) )
Expand Down Expand Up @@ -349,8 +340,8 @@ trait Iteratees {
s(el = e => {
@inline def add(res: List[A], newl: List[ResumableIter[E, F, A]], k: (Input[E]) => IterateeT[E, F, (A, IterateeT[E, F, _])]): F[(List[A], List[ResumableIter[E, F, A]])] = {
val d = k(Element(e))
val nextl = d :: newl
F.map(d.value) { step =>
val nextl = d :: newl
step(
done = (x, _) => (x._1 :: res, nextl),
cont = _ => (res, nextl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ trait PullIteratees {

type QNamesMatch = (List[QName], Option[XmlPath])

def onQNames[F[_]: Monad](qnames: List[QName]): ResumableIter[PullType, F, QNamesMatch] = onQNamesI(qnames)(ScalesXml.qnameEqual, implicitly[Monad[F]])

/**
* Collects all data belonging to an element that matches
* the list. <top><middle><ofInterest> content </ofInterest><ofInterest....
* onQNames(List("top"l, "middle"l, "ofInterest"l))
* would return an iteratee that returned every <ofInterest> content </ofInterest>
* as a path (each parent node containing only one child node).
*/
def onQNames[F[_]: Monad](qnames: List[QName])(implicit qe: Equal[QName]): ResumableIter[PullType, F, QNamesMatch] = {
def onQNamesI[F[_]](qnames: List[QName])(implicit qe: Equal[QName], F: Monad[F]): ResumableIter[PullType, F, QNamesMatch] = {

/*
* The pairs allow the depth of each element to be followed. In particular this stops both descent and ascent problems in the
Expand All @@ -53,7 +55,7 @@ trait PullIteratees {

Cont(
// is it our head?
if ((!toGo.isEmpty) && q == focus._1)
if ((!toGo.isEmpty) && q === focus._1)
// move down
step(before :+ focus, toGo.head, toGo.tail, npath, false)
else
Expand Down
2 changes: 1 addition & 1 deletion scales-xml/src/main/scala/scales/xml/xpath/Functions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ object XmlTreeText extends TextValue[XmlTree] {
if (walker.isLeft) {
walker.left.get match {
case Text(text) => sb.append(text)
case CData(text) => sb.append(text)
case CData(text) => sb.append(text)
case _ => ()
}
}
Expand Down
22 changes: 14 additions & 8 deletions scales-xml/src/test/scala/scales/utils/IterateeTests.scala
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
package scales.utils
import ScalesUtils._
import scalaz.EphemeralStream.emptyEphemeralStream
import scalaz.Free.Trampoline
import scalaz.iteratee.{EnumeratorT, IterateeT, StepT}
import scalaz.iteratee.Input.{Element, Empty, Eof, emptyInput, eofInput}
import scalaz.iteratee.Iteratee.{enumIterator, foldM, iterateeT => siteratee}
import scalaz.iteratee.StepT.{Cont, Done, scont}
import scalaz.Free._
import scalaz.{Bind, Monad}
import scalaz.{Bind, EphemeralStream, Monad}
import scalaz.Scalaz._

import scala.annotation.tailrec

object StreamHelpers {

def lTo(lower: Long, upper: Long): EphemeralStream[Long] =
if (lower > upper) emptyEphemeralStream else EphemeralStream.cons(lower, lTo(lower + 1, upper))

def iTo(lower: Int, upper: Int): EphemeralStream[Int] =
if (lower > upper) emptyEphemeralStream else EphemeralStream.cons(lower, iTo(lower + 1, upper))


}

class IterateeTest extends junit.framework.TestCase {

import junit.framework.Assert._

import StreamHelpers._
import scalaz.iteratee.{Iteratee, Enumerator, Input}
import scalaz.EphemeralStream
import EphemeralStream.emptyEphemeralStream
Expand Down Expand Up @@ -142,13 +155,6 @@ class IterateeTest extends junit.framework.TestCase {
p.run
}

def lTo(lower: Long, upper: Long): EphemeralStream[Long] =
if (lower > upper) emptyEphemeralStream else EphemeralStream.cons(lower, lTo(lower + 1, upper))

def iTo(lower: Int, upper: Int): EphemeralStream[Int] =
if (lower > upper) emptyEphemeralStream else EphemeralStream.cons(lower, iTo(lower + 1, upper))


def testEventsNotLostOneToMany = {
val i = List[Long](1,2,3,4,5).iterator

Expand Down
4 changes: 2 additions & 2 deletions scales-xml/src/test/scala/scales/xml/PullIterateTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class PullIterateTest extends junit.framework.TestCase {
}

}
/*

def testOnQNamesRepeatedQNames = {

val ourMax = maxIterations / 10 // full takes too long but does work in constant space
Expand All @@ -89,7 +89,7 @@ class PullIterateTest extends junit.framework.TestCase {
assertEquals(1, x.zipUp.children.size)
}

}*/
}

def testOnQNameEqualImplicit : Unit = {

Expand Down
Loading

0 comments on commit 7c59489

Please sign in to comment.