2

Suppose I have a list of numbers and list of functions:

val xs: List[Int] = List(1, 2, 3)
val fs: List[Int => Int] = List(f1, f2, f3)

Now I would like to use an Applicative to apply f1 to 1, f2 to 2, etc.

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

How can I do it with Scalaz ?

Michael
  • 41,026
  • 70
  • 193
  • 341
  • scalaz has applicatives. – Daenyth Jun 22 '15 at 18:47
  • What's wrong with simply doing a zip followed by a map to apply the function to its paired value? `xs.zip(fs).map(p=>p._2.apply(p._1))` – Chris Scott Jun 22 '15 at 19:34
  • @ChrisScott There is nothing wrong with `zip` and `map`. I just started feeling that this is _actually_ `<*>`. So using `<*>` instead of `zip` and `map` is _the right thing_ to do. Besides I have been learning about `Applicatives` now. – Michael Jun 23 '15 at 04:24

3 Answers3

4

pure for zip lists repeats the value forever, so it's not possible to define a zippy applicative instance for Scala's List (or for anything like lists). Scalaz does provide a Zip tag for Stream and the appropriate zippy applicative instance, but as far as I know it's still pretty broken. For example, this won't work (but should):

import scalaz._, Scalaz._

val xs = Tags.Zip(Stream(1, 2, 3))
val fs = Tags.Zip(Stream[Int => Int](_ + 3, _ + 2, _ + 1))

xs <*> fs

You can use the applicative instance directly (as in the other answer), but it's nice to have the syntax, and it's not too hard to write a "real" (i.e. not tagged) wrapper. Here's the workaround I've used, for example:

case class ZipList[A](s: Stream[A])

import scalaz._, Scalaz._, Isomorphism._

implicit val zipListApplicative: Applicative[ZipList] =
  new IsomorphismApplicative[ZipList, ({ type L[x] = Stream[x] @@ Tags.Zip })#L] {
    val iso =
      new IsoFunctorTemplate[ZipList, ({ type L[x] = Stream[x] @@ Tags.Zip })#L] {
        def to[A](fa: ZipList[A]) = Tags.Zip(fa.s)
        def from[A](ga: Stream[A] @@ Tags.Zip) = ZipList(Tag.unwrap(ga))
      }
    val G = streamZipApplicative
  }

And then:

scala> val xs = ZipList(Stream(1, 2, 3))
xs: ZipList[Int] = ZipList(Stream(1, ?))

scala> val fs = ZipList(Stream[Int => Int](_ + 10, _ + 11, _ + 12))
fs: ZipList[Int => Int] = ZipList(Stream(<function1>, ?))

scala> xs <*> fs
res0: ZipList[Int] = ZipList(Stream(11, ?))

scala> res0.s.toList
res1: List[Int] = List(11, 13, 15)

For what it's worth, it looks like this has been broken for at least a couple of years.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
1

I see a solution with streamZipApplicative :

import scalaz.std.stream._
import scalaz.Tags

val xs: List[Int] = List(1, 2, 3)
val fs: List[Int => Int] = List(f1, f2, f3)

val zippedLists = streamZipApplicative.ap(Tags.Zip(xs.toStream)) (Tags.Zip(fs.toStream))

val result = Tag.unwrap(zippedLists).toList
n1r3
  • 8,593
  • 3
  • 18
  • 19
  • Adding the specific imports would improve this answer, as well as adding any possible syntax improvements – Daenyth Jun 22 '15 at 19:11
1

Learning Scalaz spends a few paragraphs on this topic in their introduction to Applicatives. They quote LYAHFGG:

However, [(+3),(2)] <> [1,2] could also work in such a way that the first function in the left list gets applied to the first value in the right one, the second function gets applied to the second value, and so on. That would result in a list with two values, namely [4,4]. You could look at it as [1 + 3, 2 * 2].

But then adds:

This can be done in Scalaz, but not easily.

The "not easily" part uses streamZipApplicative like in @n1r3's answer:

scala> streamZipApplicative.ap(Tags.Zip(Stream(1, 2)))(Tags.Zip(Stream({(_: Int) + 3}, {(_: Int) * 2})))
res32: scala.collection.immutable.Stream[Int] with Object{type Tag = scalaz.Tags.Zip} = Stream(4, ?)

scala> res32.toList
res33: List[Int] = List(4, 4)

The "not easily" is the part that bothers me. I'd like to borrow from @Travis Brown fantastic answer. He is comparing the use of monads and applicatives (i.e. why use applicatives when you have a monad?):

Second (and relatedly), it's just a solid development practice to use the least powerful abstraction that will get the job done.

So, I would say that until a framework provides an applicative that works like your first use-case:

val ys: List[Int] = xs <*> fs

To use zip and map here instead:

xs.zip(fs).map(p=>p._2.apply(p._1))

To me, this code is much clearer and simpler than the alternatives in scalaz. This is the least powerful abstraction that gets the job done.

Community
  • 1
  • 1
Chris Scott
  • 1,721
  • 14
  • 27
  • 1
    Thank you for the answer but I don't think that `map` and `zip` is the `least powerful abstraction`. They look like just _low level_ rather than less powerful to me. – Michael Jun 23 '15 at 13:52
  • I don't disagree with you, but I would argue the lower-level abstraction is cleaner, especially if you have to jump through hoops to get the higher-level abstraction to work. – Chris Scott Jun 23 '15 at 16:45
  • 1
    I doubt it. If I need to repeat that `map` and `zip` solution _a few dozen_ times I would probably prefer to get the higher-level abstraction work _once_ and then just re-use it a few dozen times. – Michael Jun 24 '15 at 05:45
  • I suppose we are on different sides of the simplicity vs. higher-level abstraction fence, then. Anyway, thanks for the question, this was good food for thought. – Chris Scott Jun 24 '15 at 12:11