www gtm googletagmanager scala shapeless

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.