4

Assume we have two lists :

val l1=List("a","b","c")
val l2 = List("1","2","3")

What I want is : List("a1", "b2", "c3") that is, adding the nth element of l1 with the nth element of l2

A way to achieve it is :

(l1 zip l2).map (c => {c._1+c._2})

I just wonder if one could achieve it with an Applicative. I tried :

(l1 |@| l2) { _+ _ } 

but it gives all the combinations :

List(a1, a2, a3, b1, b2, b3, c1, c2, c3)

Any idea?

Thank you

Benoit

svick
  • 236,525
  • 50
  • 385
  • 514
bhericher
  • 667
  • 4
  • 13
  • You want a ziplist. Haskell has one at http://en.wikibooks.org/wiki/Haskell/Applicative_Functors#ZipLists. I'm not sure if Scalaz has one, but nor have I looked. – Derek Wyatt Feb 06 '12 at 20:50
  • @DerekWyatt : Thanks Derek. missingfaktor has provided the answer – bhericher Feb 06 '12 at 21:05

2 Answers2

5

You cannot do that with strict lists, so instead use lazy lists i.e. streams. You have to define the Applicative[Stream] instance as shown below. (You'll find it in Haskell standard library under the name ZipList.)

scala> val s1 = Stream("a", "b", "c")
s1: scala.collection.immutable.Stream[java.lang.String] = Stream(a, ?)

scala> val s2 = Stream("1", "2", "3")
s2: scala.collection.immutable.Stream[java.lang.String] = Stream(1, ?)

scala> implicit object StreamApplicative extends Applicative[Stream] {
     |   def pure[A](a: => A) = Stream.continually(a)
     |   override def apply[A, B](f: Stream[A => B], xs: Stream[A]): Stream[B] = (f, xs).zipped.map(_ apply _)
     | }
defined module StreamApplicative

scala> (s1 |@| s2)(_ + _)
res101: scala.collection.immutable.Stream[java.lang.String] = Stream(a1, ?)

scala> .force
res102: scala.collection.immutable.Stream[java.lang.String] = Stream(a1, b2, c3)

The reason this cannot be done with strict lists is because it is impossible to define a pure on them that satisfies the applicative laws.

As an aside, Scala lets you do this more concisely than the code you have used in OP:

scala> (l1, l2).zipped.map(_ + _)
res103: List[java.lang.String] = List(a1, b2, c3)
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • Thank you for your answer and explanations! As far as I understand, it works with Streams because as they are lazy, their elements are procedeed one by one. Right? – bhericher Feb 06 '12 at 21:02
  • @missinglake : Wah! You're far from being "lazy" on this forum! That is certainly "Reactive programming" :) – bhericher Feb 06 '12 at 21:08
  • @bhericher, heh, thanks. :) By the way, it's missingfaktor, not missinglake. – missingfaktor Feb 06 '12 at 21:12
3

The answer is that you can't achieve this with an applicative as far as I can see. The applicative for list will apply the function to all combinations, as you have found out. Not great for what you want but awesome for stuff like creating cartesian products.

A slightly less verbose method might use Tuple2W.fold supplied by scalaz:

(l1 zip l2).map (_ fold (_ + _))
oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449