2013-04-21 5 views
17

Bei einem formlosen HList, bei dem jedes Listenelement denselben Typenkonstruktor verwendet, wie kann der HList sequenziert werden?Sequenzierung eines HLists

Zum Beispiel:

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

Ich versuchte Sequenz wie folgt umzusetzen:

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

Aber das nicht kompiliert:

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

Irgendwelche Tipps zu diesem Thema für die Umsetzung entweder ein spezifisches Beispiel wie Option oder für einen beliebigen Applicative?

Antwort

17

Sie waren ziemlich eng Sie müssen nur sicherstellen, dass Sie das Extra an Beweise haben, dass es für fragt: wie diese

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

Oder, wenn Sie wollen etwas allgemeinere und haben eine applicative Implementierung :

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

können Sie schreiben:

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) 

Und jetzt können Sie Sequen Listen, usw., (vorausgesetzt, Sie haben die entsprechenden Instanzen).


Update: Beachten Sie, dass ich den Rückgabetyp Anmerkung für sequence in beiden Fällen verzichtet habe. Wenn wir es wieder einsetzen, Drosseln der Compiler:

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

Dies liegt daran, die RightFolder Instanz trägt um seinen Rückgabetyp als abstraktes Art Mitglied. Wir wissen, dass es in diesem Fall F[M] ist, aber der Compiler kümmert sich nicht darum, was wir wissen.

Wenn wir sein explizit über den Rückgabetyp der Lage sein wollen, können wir die RightFolderAux Instanz verwenden statt:

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) 

Beachten Sie, dass RightFolderAux einen zusätzlichen Typ-Parameter hat, die den Rückgabetyp angibt.

+2

Danke! Ich habe versucht, den impliziten RightFolder vor dem Posten zur Verfügung zu stellen, bin aber zu dem genauen Fehler gelaufen, den Sie oben angegeben haben (benötigt 'F [M]', aber gefunden 'folder.Out'). Der RightFolderAux macht das klar. – mpilquist

+0

Ich habe gerade die Sequenz-Implementierung mit Shapeless 2.0 versucht und diesen Fehler erhalten: Fehler: (41, 36) konnte keinen impliziten Wert für den Parameterordner finden: formeless.ops.hlist.RightFolder [L, Option [formless.HNil], optionFolder. type] l.foldRight (Option (HNil: HNil)) (optionFolder) ^ –

+1

@ChanningWalton: Gibt es davor einen Fehler? Haben Sie 'shapeless.ops.hlist.RightFolder' importiert? Es funktioniert für mich in 2.0.0. –

1

Jetzt können Sie kittenscats.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) 

Das Beispiel gegen Funktion sequenziert verwenden, aber Sie können alles verwenden, die eine Katzen Applicative Instanz hat.

Verwandte Themen