Cartesian product traverse in scalaz

前端 未结 4 826
逝去的感伤
逝去的感伤 2021-02-10 03:06

In Eric Torreborre\'s blogpost on the paper Essence of the Iterator Pattern, he describes how the cartesian product of a traverse is also a traverse.

Can anyone show me

4条回答
  •  天涯浪人
    2021-02-10 03:36

    I'm adding my own answer, building on Jason's one, to show different ways of traversing the list:

    import org.specs2._
    import scalaz.std.anyVal._, scalaz.std.list._
    import scalaz._, std.tuple._
    import scalaz.{Monoid, Applicative}
    
    class TraverseSpec extends mutable.Specification {
    
      implicit val Sum = Monoid[Int].applicative
      implicit val Concat = Monoid[List[String]].applicative
      implicit val A: Applicative[({type λ[α] = (Int, List[String])})#λ] = Sum.product[({type λ[α]=List[String]})#λ](Concat)
      val xs = List(1, 2, 3, 4)
    
      "traverse - by folding the list with a Monoid" >> {
        val (sum, text) = Foldable[List].foldMap(xs)(a => (a, List(a.toString + "Z")))
        (sum, text) === (10, List("1Z", "2Z","3Z", "4Z"))
      }
      "traverse - with a function returning a tuple" >> {
        val (sum, text) = A.traverse(xs)(a => (a, List(a.toString + "Z")))
        (sum, text.reverse) === (10, List("1Z", "2Z","3Z", "4Z"))
      }
      "traverse - with 2 functions and 2 traversals" >> {
        val count   = (a: Int) => a
        val collect = (a: Int) => List(a.toString+"Z")
    
        val sum  = Sum.traverse(xs)(count)
        val text = Concat.traverse(xs)(collect)
    
        (sum, text.reverse) === (10, List("1Z", "2Z","3Z", "4Z"))
      }
      "traverse - with 2 functions and 1 fused traversal" >> {
        val sum     = (a: Int) => a
        val collect = (a: Int) => List(a.toString+"Z")
    
        implicit def product[A, B, C](f: A => B): Product[A, B] = Product(f)
        case class Product[A, B](f: A => B) {
          def <#>[C](g: A => C) = (a: A) => (f(a), g(a))
        }
    
        val (total, text)  = A.traverse(xs)(sum <#> collect)
        (total, text.reverse) === (10, List("1Z", "2Z","3Z", "4Z"))
      }
    }
    

    I think that the last example shows what you're after: 2 independently defined functions which can be composed to do just one traversal.

提交回复
热议问题