Список Клейсли Клейсли списка

Мне было интересно, если есть способ повернуть List[Kleisli[Option, Int, Int]] в Kleisli[Option, Int, List[Int]],

В частности, у меня есть список kleisli, сформированный следующим образом:

def k(a: String) = Kleisli[Option, Int, Int](m => Some(a.length * m))
val kList = List("hi", "hello").map(k)

Что я делаю, это следующее

Kleisli[Option, Int, List[Int]](m => kList.map(_.run(m)).sequence)

что очень грязно, не выразительно и требует много ручной работы.

Есть ли способ лучше?

3 ответа

Решение

Да, вы можете использовать traverse который делает именно это. Если вы используете cats <= 0.9.0 вы можете использовать следующий код:

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)

Если вы используете Scala 2.11.9+, добавив scalacOptions += "-Ypartial-unification" на ваш build.sbt файл, вы можете просто использовать traverse на месте traverseU, Кроме того, начиная с версии 1.0.0, traverseU а также sequenceU больше не будет существовать

Обратите внимание, что если вы используете Scala < 2.11.9, но>= 2.10.6, вы все равно можете включить частичное объединение, добавив этот плагин в свою сборку.

Самое простое, что вы можете сделать, это иметь partial-unification включен и использует traverse:

import cats.implicits._

List("hi", "hello").traverse(k)

Это так же, как работает sequence на ваше kList, как traverse эквивалентно map а потом sequence,

Самый простой способ включить partial-unification, это добавить sbt-partial-unification плагин.

Если вы используете Scala 2.11.9 или новее, вы также можете просто добавить флаг компилятора:

scalacOptions += "-Ypartial-unification"

Мы из cats Команда настоятельно рекомендует вам постоянно включать этот флаг при использовании кошек, так как он делает все намного проще.

С помощью TraverseOps.sequence мы можем преобразовать List[A[B]] в A[List[B]], где

A = ({type λ[α] = Kleisli[Option, Int, α]})#λ
B = Int

Итак, ответ:

def transform(x: List[Kleisli[Option, Int, Int]]) =
  x.sequence[({type λ[α] = Kleisli[Option, Int, α]})#λ, Int]

Следующий код является полным решением:

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

Другие вопросы по тегам