1

This is a follow-up to one of my recent previous questions:

I would like to define a zip Applicative instance for List (and probably Set and Map). For example:

val xs: List[Int] = List(1, 2, 3)
val fs: List[Int => Int] = List(f1, f2, f3)
val ys: List[Int] = xs <*> fs // expected to be List(f1(1), f2(2), f3(3))

So I defined a ZipList and its Applicative:

case class ZipList[A](val list: List[A])

implicit val zipListApplicative = new Applicative[ZipList] {

  def point[A](a: => A): ZipList[A] = ZipList(List(a))

  def ap[A, B](za: => ZipList[A])(zf: => ZipList[A => B]): ZipList[B] = {
    val bs = (za.list zip zf.list) map {case (a, f) => f(a)}
    ZipList(bs)
  }
}

and can use it as follows:

 scala> val xs: List[Int] = List(1, 2, 3)
 xs: List[Int] = List(1, 2, 3)

 scala> val fs: List[Int => Int] = List(_ + 2, _ + 2, _ +1)
 fs: List[Int => Int] = List(<function1>, <function1>, <function1>)

 scala> ZipList(xs) <*> ZipList(fs)
 res4: ZipList[Int] = ZipList(List(3, 4, 4))

It seems to be working but maybe I am missing something.

  • Does zipListApplicative comply to the applicative laws ?
  • Is ZipList supposed to be a stream because the point should generate an infinite stream of values ? Why ?
Community
  • 1
  • 1
Michael
  • 41,026
  • 70
  • 193
  • 341

1 Answers1

4

Applicatives should satisfy the law

point identity <*> v == v

which yours does not since

point identity List(1,2,3) == List(1)

pure a for a zip list should return an infinite stream of a which is why you need a lazy data structure.

Lee
  • 142,018
  • 20
  • 234
  • 287
  • I see it now, thank you. Could you give an example what can go wrong if my _quasi_-applicative does not satisfy the identity law ? – Michael Jun 29 '15 at 10:33
  • 2
    @Michael For example, `ZipList(List(1, 2, 3)).map(_ + 1)` will return `ZipList(List(2))` instead of `ZipList(List(2, 3, 4))` – ZhekaKozlov Jun 30 '15 at 12:13