Scalaz中能否反转List的Applicative?F[List[A]]可转为List[F[A]]吗
Awesome question! This gets into some neat core concepts in Scalaz and functional programming, so let's break it down.
First, quick recap of what you already know: turning List[F[A]] into F[List[A]] is done with traverse (or sequence, which is the simpler case when you just need to flip the structure). That works as long as F is an Applicative—makes sense, since you need to combine the F values into a single F of the list.
Now, the reverse: converting F[List[A]] to List[F[A]]. The short answer is yes, but not for every possible F. The ability to do this is tied to a Scalaz type class called Distributive.
What's Distributive all about?
Think of Distributive as the dual of Traverse. Where Traverse lets you "gather" a collection of Fs into one F holding the collection, Distributive lets you "spread out" an F that holds a collection into a collection holding Fs. Its main method is distribute, which looks like this:
def distribute[G[_]: Functor, A](fga: F[G[A]]): G[F[A]]
Since List is a Functor, plugging it in for G gives you exactly the conversion you're asking for: F[List[A]] → List[F[A]].
Let's see it in action with common types
Let's use some familiar F instances to make this concrete:
- Option: If you have
Some(List(1, 2, 3)), callingdistributewill turn it intoList(Some(1), Some(2), Some(3)). ForNone, since there's no underlying list to work with, you get an emptyList[Option[Int]]—that makes sense, becauseNonemeans there's no elements to wrap inOption. - Either[E, *]: A
Right(List("foo", "bar"))becomesList(Right("foo"), Right("bar")). ALeft("oops")turns into an empty list, same logic asOption. - Reader[Env, *]: A
Reader[Env, List[A]]is just a function that takes an environment and returns a list. Distributing this gives you aList[Reader[Env, A]]—each element in the list is a reader that uses the same environment to return one element from the original list's result.
When can't you do this?
If F doesn't have a Distributive instance, there's no lawful, generic way to make this conversion. For example, take Future: you can't turn a Future[List[A]] into a List[Future[A]] because the Future might not have resolved yet. You can't split a single pending future into multiple futures that each resolve to one element—you'd need to know the list's contents before the future completes, which isn't possible.
Using it in your code
To use distribute, just import Scalaz's implicits and call the method on your F[List[A]]:
import scalaz._ import Scalaz._ // Example with Option val someNumbers: Option[List[Int]] = Some(List(1, 2, 3)) val numbersInOption: List[Option[Int]] = someNumbers.distribute // Result: List(Some(1), Some(2), Some(3)) val noNumbers: Option[List[Int]] = None val emptyOptionList: List[Option[Int]] = noNumbers.distribute // Result: List()
Final takeaway
So to wrap up: You absolutely can convert F[List[A]] to List[F[A]] in Scalaz, but only when F implements the Distributive type class. It's the exact dual operation to the sequence/traverse you already know, and it's designed for exactly this kind of structure flipping.
内容的提问来源于stack exchange,提问作者St.Antario

