2013-04-21 4 views
17

Учитывая, что в элементе Shapeless HList, где каждый элемент списка имеет один и тот же тип конструктора, как HList может быть упорядочен?Последовательность HList

Например:

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

Я пытался реализовать последовательность так:

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

Но это не компилируется:

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

Любые советы по реализации этого для либо конкретный пример, например Option или для произвольного аппликативного?

ответ

17

Вы были очень близко, вы просто должны убедиться, что у вас есть дополнительный бит доказательства того, что он с просьбой о:

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

Или, если вы хотите что-то более общее, и есть аппликативная реализация, как это :

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

вы можете написать:

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) 

И теперь вы можете sequen ce списки и т. д. (при условии, что у вас есть соответствующие экземпляры).


Update: Обратите внимание, что я опустил возвращение аннотацию типа для sequence в обоих случаях здесь. Если положить его обратно, компилятор дроссели:

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

Это происходит потому, что экземпляр RightFolder таскает его возвращаемый тип в качестве члена абстрактного типа. Мы знаем, что в этом случае это F[M], но компилятор не заботится о том, что мы знаем.

Если мы хотим, чтобы иметь возможность быть явным о типе возврата, мы можем использовать экземпляр RightFolderAux вместо:

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) 

Обратите внимание, что RightFolderAux имеет дополнительный параметр типа, который указывает тип возвращаемого значения.

+2

Спасибо! Я попытался предоставить неявный RightFolder перед публикацией, но столкнулся с точной ошибкой, о которой вы указали выше (требуется 'F [M]', но найден 'folder.Out'). RightFolderAux делает это понятным. – mpilquist

+0

Я просто попробовал реализацию последовательности с Shapeless 2.0 и получил эту ошибку: Ошибка: (41, 36) не удалось найти неявное значение для папки параметров: shapeless.ops.hlist.RightFolder [L, Option [shapeless.HNil], optionFolder. тип] l.foldRight (опция (HNil: HNil)) (опцияFolder) ^ –

+1

@ChanningWalton: Есть ли ошибка перед этим? Вы импортировали 'shapeless.ops.hlist.RightFolder'? Это работает для меня в 2.0.0. –

1

Теперь вы можете использовать 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) 

В примере секвенировал против функции, но вы можете использовать что-нибудь, что имеет кошка Applicative экземпляра.

Смежные вопросы