Sequencing an HList

梦想的初衷 提交于 2019-11-27 12:59:12

问题


Given a Shapeless HList where every list element shares the same type constructor, how can the HList be sequenced?

For example:

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] =
  ???

I tried to implement sequence like this:

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)
}

But that does not compile:

could not find implicit value for parameter folder: shapeless.RightFolder[L,Option[shapeless.HNil.type],SequencingHList.optionFolder.type]

Any tips on implementing this for either a specific example like Option or for an arbitrary Applicative?


回答1:


You were pretty close—you just need to make sure you have the extra bit of evidence that it's asking for:

def sequence[L <: HList : *->*[Option]#λ, M <: HList](l: L)(implicit
  folder: RightFolder[L, Option[HNil], optionFolder.type]
) = l.foldRight(some(HNil: HNil))(optionFolder)

Or, if you want something more general, and have an applicative implementation like this:

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)
}

You can write:

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)

And now you can sequence lists, etc., as well (assuming you have the appropriate instances).


Update: Note that I've omitted the return type annotation for sequence in both cases here. If we put it back in, the compiler chokes:

<console>:18: error: type mismatch;
 found   : folder.Out
 required: F[M]

This is because the RightFolder instance carries around its return type as an abstract type member. We know it's F[M] in this case, but the compiler doesn't care about what we know.

If we want to be able to be explicit about the return type, we can use the RightFolderAux instance instead:

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)

Note that RightFolderAux has an extra type parameter, which indicates the return type.




回答2:


Now you can use 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)

The example sequenced against Function but you can use anything that has an cats Applicative instance.



来源:https://stackoverflow.com/questions/16127360/sequencing-an-hlist

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!