scala - googletagmanager - gtm console
Secuenciar un HList (2)
Dada una lista HL sin forma donde cada elemento de la lista comparte el mismo tipo de constructor, ¿cómo se puede secuenciar la lista HL?
Por ejemplo:
def some[A](a: A): Option[A] = Some(a)
def none[A]: Option[A] = None
val x = some(1) :: some("test") :: some(true) :: HNil
val y = sequence(x) // should be some(1 :: "test" :: true :: HNil)
def sequence[L <: HList : *->*[Option]#λ, M <: HList](l: L): Option[M] =
???
Intenté implementar una secuencia como esta:
object optionFolder extends Poly2 {
implicit def caseOptionValueHList[A, B <: HList] = at[Option[A], Option[B]] { (a, b) =>
for { aa <- a; bb <- b } yield aa :: bb
}
}
def sequence[L <: HList : *->*[Option]#λ, M <: HList](l: L): Option[M] = {
l.foldRight(some(HNil))(optionFolder)
}
Pero eso no compila:
could not find implicit value for parameter folder: shapeless.RightFolder[L,Option[shapeless.HNil.type],SequencingHList.optionFolder.type]
¿Algún consejo sobre cómo implementar esto para un ejemplo específico como una Opción o para un Aplicativo arbitrario?
Ahora puedes usar kittens cats.sequence
import cats.implicits._
import cats.sequence._
import shapeless._
val f1 = (_: String).length
val f2 = (_: String).reverse
val f3 = (_: String).toDouble
val f = (f1 :: f2 :: f3 :: HNil).sequence
assert( f("42.0") == 4 :: "0.24" :: 42.0 :: HNil)
El ejemplo se secuenció en función de la función, pero puede usar cualquier cosa que tenga una instancia Applicative
gatos.
Estuviste muy cerca, solo necesitas asegurarte de que tienes la evidencia adicional que solicita:
def sequence[L <: HList : *->*[Option]#λ, M <: HList](l: L)(implicit
folder: RightFolder[L, Option[HNil], optionFolder.type]
) = l.foldRight(some(HNil: HNil))(optionFolder)
O, si quieres algo más general, y tienes una implementación aplicativa como esta:
trait Applicative[F[_]] {
def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B]
def point[A](a: => A): F[A]
def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(point(f))
}
implicit object optionApplicative extends Applicative[Option] {
def ap[A, B](fa: => Option[A])(f: => Option[A => B]) = f.flatMap(fa.map)
def point[A](a: => A) = Option(a)
}
Puedes escribir:
object applicativeFolder extends Poly2 {
implicit def caseApplicative[A, B <: HList, F[_]](implicit
app: Applicative[F]
) = at[F[A], F[B]] {
(a, b) => app.ap(a)(app.map(b)(bb => (_: A) :: bb))
}
}
def sequence[F[_]: Applicative, L <: HList: *->*[F]#λ, M <: HList](l: L)(implicit
folder: RightFolder[L, F[HNil], applicativeFolder.type]
) = l.foldRight(implicitly[Applicative[F]].point(HNil: HNil))(applicativeFolder)
Y ahora también puede secuenciar listas, etc. (suponiendo que tenga las instancias apropiadas).
Actualización: Tenga en cuenta que he omitido la anotación de tipo de retorno para la sequence
en ambos casos aquí. Si lo volvemos a poner, el compilador se ahoga:
<console>:18: error: type mismatch;
found : folder.Out
required: F[M]
Esto se debe a que la instancia de RightFolder
lleva su tipo de retorno como un miembro de tipo abstracto. Sabemos que es F[M]
en este caso, pero al compilador no le importa lo que sabemos.
Si queremos poder ser explícitos sobre el tipo de retorno, podemos usar la instancia de RightFolderAux
lugar:
def sequence[F[_]: Applicative, L <: HList: *->*[F]#λ, M <: HList](l: L)(implicit
folder: RightFolderAux[L, F[HNil], applicativeFolder.type, F[M]]
): F[M] =
l.foldRight(implicitly[Applicative[F]].point(HNil: HNil))(applicativeFolder)
Tenga en cuenta que RightFolderAux
tiene un parámetro de tipo extra, que indica el tipo de retorno.