2013-04-21 9 views
17

Biorąc pod uwagę Shapeless HList, gdzie każdy element listy współużytkuje ten sam typ konstruktora, w jaki sposób HList może być sekwencjonowane?Sequencing a HList

Na przykład:

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

starałem się wdrożyć sekwencję tak:

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

Ale to nie skompilować:

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

jakieś wskazówki na wdrożenie tego do albo konkretny przykład, taki jak Option czy arbitralny Applicative?

Odpowiedz

17

Byłaś całkiem blisko, wystarczy upewnić się, że dodatkowy kawałek dowodów, że to z prośbą o:

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

Albo, jeśli chcesz coś bardziej ogólny i mają aplikacyjny realizację takiego :

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

można napisać:

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) 

I teraz można sequen pl itd. (zakładając, że masz odpowiednie instancje).


Aktualizacja: Zauważ, że ja pominięty zwracany typ adnotacji dla sequence tu obu przypadkach. Jeśli stawiamy ją z powrotem, kompilator dławiki:

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

To dlatego, że instancja RightFolder prowadzi wokół jego typ zwracany jako abstrakcyjne członka typu. Wiemy, że w tym przypadku jest to F[M], ale kompilator nie dba o to, co wiemy.

Jeśli chcemy, aby móc być jawne o rodzaju powrotnego, możemy użyć instancji RightFolderAux zamiast:

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) 

Zauważ, że RightFolderAux ma dodatkowy parametr typu, co wskazuje na typ zwracany.

+2

Dzięki! Próbowałem dostarczyć ukryty RightFolder przed wysłaniem, ale podbiegł dokładnie do błędu, który wskazałeś powyżej (wymagane 'F [M]', ale znalazłem 'folder.Out'). RightFolderAux wyjaśnia to. – mpilquist

+0

Właśnie próbowałem implementacji sekwencji z Shapeless 2.0 i otrzymałem ten błąd: Błąd: (41, 36) nie mógł znaleźć niejawnej wartości dla folderu parametrów: shapeless.ops.hlist.RightFolder [L, Option [shapeless.HNil], optionFolder. type] l.foldRight (Opcja (HNil: HNil)) (opcjaFolder) ^ –

+1

@ChanningWalton: Czy wystąpił błąd przed tym? Czy zaimportowałeś 'shapeless.ops.hlist.RightFolder'? Działa dla mnie w wersji 2.0.0. –

1

Teraz można użyć 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) 

Ten przykład zsekwencjonowany przed funkcji, ale można użyć wszystkiego, co ma koty Applicative instancji.

Powiązane problemy