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
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With