/tests/src/test/scala/scalaz/TraverseTest.scala

http://github.com/scalaz/scalaz · Scala · 164 lines · 135 code · 24 blank · 5 comment · 35 complexity · fcc52526bdd579c8fad1793ea2aacaad MD5 · raw file

  1. package scalaz
  2. import org.scalacheck.Prop.forAll
  3. object TraverseTest extends SpecLite {
  4. import scalaz._
  5. import scalaz.State._
  6. import std.AllInstances._
  7. import std.AllFunctions._
  8. import syntax.traverse._
  9. "list" should {
  10. // ghci> import Data.Traversable
  11. // ghci> import Control.Monad.Writer
  12. // ghci> let (|>) = flip ($)
  13. // ghci> traverse (\x -> writer (x, x)) ["1", "2", "3"] |> runWriter
  14. // (["1","2","3"],"123")
  15. "apply effects in order" in {
  16. val s: Writer[String, List[Int]] = List(1, 2, 3).traverseU(x => Writer(x.toString, x))
  17. s.run must_===(("123", List(1, 2, 3)))
  18. }
  19. "indexed" ! forAll { xs: List[Byte] =>
  20. Traverse[List].indexed(xs) must_=== xs.zipWithIndex.map{case (a, b) => (b, a)}
  21. }
  22. "traverse through option effect" in {
  23. val s: Option[List[Int]] = List(1, 2, 3).traverseU((x: Int) => if (x < 3) some(x) else none)
  24. s must_===(none[List[Int]])
  25. }
  26. "traverse int function as monoidal applicative" in {
  27. val s: Const[Int, _] = List(1, 2, 3) traverseU {a => Const(a + 1)}
  28. s.getConst must_===(9)
  29. }
  30. "not blow the stack" in {
  31. val s: Option[List[Int]] = List.range(0, 32 * 1024).traverseU(x => some(x))
  32. s.map(_.take(3)) must_===(some(List(0, 1, 2)))
  33. }
  34. "be stack-safe and short-circuiting" in {
  35. val N = 10000
  36. val s: Maybe[List[Int]] = List.range(0, N) traverse { x =>
  37. if(x < N-2) Maybe.just(x)
  38. else if(x == N-2) Maybe.empty
  39. else sys.error("BOOM!")
  40. }
  41. s must_=== Maybe.empty
  42. }
  43. "state traverse agrees with regular traverse" in {
  44. val N = 10
  45. List.range(0,N).traverseS(x => modify((x: Int) => x+1))(0) must_=== (
  46. List.range(0,N).traverseU(x => modify((x: Int) => x+1)).apply(0))
  47. }
  48. "state traverse does not blow stack" in {
  49. val N = 10000
  50. val s = List.range(0,N).traverseS(x => modify((x: Int) => x+1))
  51. s.exec(0) must_=== (N)
  52. }
  53. }
  54. "ilist" should {
  55. "be stack-safe and short-circuiting" in {
  56. val N = 10000
  57. val s: Maybe[IList[Int]] = IList.fromList(List.range(0, N)) traverse { x =>
  58. if(x < N-2) Maybe.just(x)
  59. else if(x == N-2) Maybe.empty
  60. else sys.error("BOOM!")
  61. }
  62. s must_=== Maybe.empty
  63. }
  64. }
  65. "stream" should {
  66. "apply effects in order" in {
  67. val s: Writer[String, Stream[Int]] = Stream(1, 2, 3).traverseU(x => Writer(x.toString, x))
  68. s.run must_===(("123", Stream(1, 2, 3)))
  69. }
  70. "be stack-safe and short-circuiting" in {
  71. val N = 10000
  72. val s: Maybe[Stream[Int]] = Stream.from(0) traverse { x =>
  73. if(x < N-2) Maybe.just(x)
  74. else if(x == N-2) Maybe.empty
  75. else sys.error("BOOM!")
  76. }
  77. s must_=== Maybe.empty
  78. }
  79. }
  80. "ephemeralstream" should {
  81. "be stack-safe and short-circuiting" in {
  82. val N = 10000
  83. val s: Maybe[EphemeralStream[Int]] = EphemeralStream.fromStream(Stream.from(0)) traverse { x =>
  84. if(x < N-2) Maybe.just(x)
  85. else if(x == N-2) Maybe.empty
  86. else sys.error("BOOM!")
  87. }
  88. s must_=== Maybe.empty
  89. }
  90. }
  91. "nonemptylist" should {
  92. "be stack-safe and short-circuiting" in {
  93. val N = 10000
  94. val fa = NonEmptyList.nel(0, IList.fromList(List.range(1, N)))
  95. val s: Maybe[NonEmptyList[Int]] = Traverse1[NonEmptyList].traverse1 (fa) ({ x =>
  96. if(x < N-2) Maybe.just(x)
  97. else if(x == N-2) Maybe.empty
  98. else sys.error("BOOM!")
  99. })
  100. s must_=== Maybe.empty
  101. }
  102. }
  103. "combos" should {
  104. "traverse with monadic join" in {
  105. val s: Writer[String, List[Int]] = List(1, 2, 3).traverseM[Writer[String, *], Int](x => Writer(x.toString, List(x, x * 2)))
  106. s.run must_===(("123", List(1, 2, 2, 4, 3, 6)))
  107. }
  108. }
  109. "derived functions" should {
  110. "sequence" in {
  111. some(List(1, 2, 3)).sequence must_===(List(some(1), some(2), some(3)))
  112. List(some(1), some(2)).sequence must_===(some(List(1, 2)))
  113. List(some(1), none[Int]).sequence must_===(none)
  114. val states: List[State[Int, Int]] = List(State.modify[Int](_ + 1).map(_ => 0), for {
  115. i <- State.get[Int]
  116. _ <- State.put(i + 1)
  117. } yield i)
  118. val state: State[Int, List[Int]] = states.sequenceU
  119. state.run(0) must_===(2 -> List(0, 1))
  120. List(some(List(1, 2)), some(List(3, 4, 5))).sequenceM must_===(some(List(1, 2, 3, 4, 5)))
  121. List(some(List(1, 2)), none[List[Int]]).sequenceM must_===(none)
  122. }
  123. "reverse" in {
  124. Traverse[List].reverse(List(1, 2, 3)) must_===(List(3, 2, 1))
  125. }
  126. "mapAccumL/R" ! forAll {
  127. val L = Traverse[List]; import L.traverseSyntax._
  128. (l: List[Int]) => {
  129. val (acc, l2) = l.mapAccumL(List[Int]())((acc,a) => (a :: acc, a))
  130. val (acc2, l3) = l.mapAccumR(List[Int]())((acc,a) => (a :: acc, a))
  131. acc == l.reverse && l2 == l && acc2 == l3 && l3 == l
  132. }
  133. }
  134. "double reverse" ! forAll {
  135. (is: List[Int]) =>
  136. import syntax.monoid._
  137. Endo(Traverse[List].reverse[Int]).multiply(2).apply(is) must_===(is)
  138. }
  139. }
  140. }