I was wondering if there is a way to turn List[Kleisli[Option, Int, Int]]
to Kleisli[Option, Int, List[Int]]
.
In particular I have the list of kleisli formed like this:
def k(a: String) = Kleisli[Option, Int, Int](m => Some(a.length * m))
val kList = List("hi", "hello").map(k)
What I do is the following
Kleisli[Option, Int, List[Int]](m => kList.map(_.run(m)).sequence)
which is very messy, not expressive and requires a lot of manual work.
Is there a better way?
Yes, you can use traverse
which does just that. If you're using cats
<= 0.9.0 you can use the following code:
import cats.data._
import cats.instances.list._
import cats.instances.option._
import cats.syntax.traverse._
// ...
def k(a: String) = Kleisli[Option, Int, Int](m => Some(a.length * m))
val result: Kleisli[Option, Int, List[Int] = List("hi", "hello").traverseU(k)
If you're using Scala 2.11.9+, by adding scalacOptions += "-Ypartial-unification"
to your build.sbt
file, you can just use traverse
in place of traverseU
. Also, starting from version 1.0.0, traverseU
and sequenceU
will no longer exist.
Note that, if you're using Scala < 2.11.9 but >= 2.10.6 you can still enable partial unification by adding this plugin to your build.
The simplest you can do is to have partial-unification
enabled and using traverse
:
import cats.implicits._
List("hi", "hello").traverse(k)
This is the same as running sequence
on your kList
, as traverse
is equivalent to map
and then sequence
.
The easiest way to enable partial-unification
, is to add the sbt-partial-unification
plugin.
If you're on Scala 2.11.9 or newer, you can also simply add the compiler flag:
scalacOptions += "-Ypartial-unification"
We from the cats
team strongly encourage you to have this flag on at all times when using cats, as it makes everything just a lot easier.
Using TraverseOps.sequence
we can transform List[A[B]]
to A[List[B]]
, where
A = ({type λ[α] = Kleisli[Option, Int, α]})#λ
B = Int
So the answer is:
def transform(x: List[Kleisli[Option, Int, Int]]) =
x.sequence[({type λ[α] = Kleisli[Option, Int, α]})#λ, Int]
Following code is complete solution:
import scalaz._
import Scalaz._
import scalaz.Kleisli._
def transform(x: List[Kleisli[Option, Int, Int]]) = x.sequence[({type λ[α] = Kleisli[Option, Int, α]})#λ, Int]
def k(a: String) = Kleisli[Option, Int, Int](m => Some(a.length * m))
val kList = List("hi", "hello").map(k)
val res = transform(kList)
res.run(10)
https://scastie.scala-lang.org/2uZvWWb1ScOHNA55QOcWQA
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