Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 133 lines (112 sloc) 4.35 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
package scalaz
import org.scalacheck.Prop.forAll


object TraverseTest extends SpecLite {

  import scalaz._
  import scalaz.State._
  import std.AllInstances._
  import std.AllFunctions._
  import syntax.traverse._

  "list" should {
    // ghci> import Data.Traversable
    // ghci> import Control.Monad.Writer
    // ghci> let (|>) = flip ($)
    // ghci> traverse (\x -> writer (x, x)) ["1", "2", "3"] |> runWriter
    // (["1","2","3"],"123")
    "apply effects in order" in {
      val s: Writer[String, List[Int]] = List(1, 2, 3).traverseU(x => Writer(x.toString, x))
      s.run must_===(("123", List(1, 2, 3)))
    }

    "traverse through option effect" in {
      val s: Option[List[Int]] = List(1, 2, 3).traverseU((x: Int) => if (x < 3) some(x) else none)
      s must_===(none[List[Int]])
    }

    "traverse int function as monoidal applicative" in {
      val s: Int = List(1, 2, 3) traverseU {_ + 1}
      s must_===(9)
    }

    "not blow the stack" in {
      val s: Option[List[Int]] = List.range(0, 32 * 1024).traverseU(x => some(x))
      s.map(_.take(3)) must_===(some(List(0, 1, 2)))
    }

    "state traverse agrees with regular traverse" in {
      var N = 10
      List.range(0,N).traverseS(x => modify((x: Int) => x+1))(0) must_=== (
      List.range(0,N).traverseU(x => modify((x: Int) => x+1)).apply(0))
    }

    "state traverse does not blow stack" in {
      val N = 10000
      val s = List.range(0,N).traverseS(x => modify((x: Int) => x+1))
      s.exec(0) must_=== (N)
    }

    "sequenceS, traverseS, traversalS does not blow stack" in {
      val N = 100000
      val F = new Traverse[List]{
        def traverseImpl[G[_]: Applicative, A, B](fa: List[A])(f: A => G[B]) =
          Traverse[List].traverseImpl(fa)(f)
      }
      val s = List.fill(N)(modify((_: Int) + 1))
      F.sequenceS(s).exec(0) must_=== N
      F.traverseS(s)(x => x).exec(0) must_=== N
      F.traversalS[Int].run(s)(x => x).exec(0) must_=== N
    }
  }

  "stream" should {
    "apply effects in order" in {
      val s: Writer[String, Stream[Int]] = Stream(1, 2, 3).traverseU(x => Writer(x.toString, x))
      s.run must_===(("123", Stream(1, 2, 3)))
    }

    // ghci> import Data.Traversable
    // ghci> traverse (\x -> if x < 3 then Just x else Nothing) [1 ..]
    // Nothing
    "allow partial traversal" in {
      val stream = Stream.from(1)
      val s: Option[Stream[Int]] = stream.traverseU((x: Int) => if (x < 3) some(x) else none)
      s must_===(none)
    }
  }

  "combos" should {
    "traverse large stream over trampolined StateT including IO" in {
      // Example usage from Eric Torreborre
      import scalaz.effect._

      val as = Stream.range(0, 100000)
      val state: State[Int, IO[Stream[Int]]] = as.traverseSTrampoline[IO, Int, Int](a => for {
        s <- State.get[Int]
        _ <- State.put(a)
      } yield IO(a - s))
      state.eval(0).unsafePerformIO().take(3) must_===(Stream(0, 1, 1))
    }

    "traverse with monadic join" in {
      val s: Writer[String, List[Int]] = List(1, 2, 3).traverseM[({ type λ[α] = Writer[String, α] })#λ, Int](x => Writer(x.toString, List(x, x * 2)))
      s.run must_===(("123", List(1, 2, 2, 4, 3, 6)))
    }
  }

  "derived functions" should {
    "sequence" in {
      some(List(1, 2, 3)).sequence must_===(List(some(1), some(2), some(3)))
      List(some(1), some(2)).sequence must_===(some(List(1, 2)))
      List(some(1), none[Int]).sequence must_===(none)

      val states: List[State[Int, Int]] = List(State.modify[Int](_ + 1).map(_ => 0), for {
          i <- State.get[Int]
          _ <- State.put(i + 1)
        } yield i)
      val state: State[Int, List[Int]] = states.sequenceU
      state.run(0) must_===(2, (List(0, 1)))
    }

    "reverse" in {
      Traverse[List].reverse(List(1, 2, 3)) must_===(List(3, 2, 1))
    }

    "mapAccumL/R" ! forAll {
      val L = Traverse[List]; import L.traverseSyntax._
      (l: List[Int]) => {
        val (acc, l2) = l.mapAccumL(List[Int]())((acc,a) => (a :: acc, a))
        val (acc2, l3) = l.mapAccumR(List[Int]())((acc,a) => (a :: acc, a))
        acc == l.reverse && l2 == l && acc2 == l3 && l3 == l
      }
    }

    "double reverse" ! forAll {
      (is: List[Int]) =>
        import syntax.monoid._
        Endo(Traverse[List].reverse[Int]).multiply(2).apply(is) must_===(is)
    }
  }
}
Something went wrong with that request. Please try again.