2013-04-21 19 views
17

シェイプレスな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) 
} 

しかし、それはコンパイルされません:Aのいずれかのためにこれを実装する上

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

任意のヒントOptionのような具体的な例や、任意のApplicativeについて?

あなたはかなり近い、あなたはちょうどあなたがそれが求めていたという証拠の余分なビットを持っていることを確認する必要がありました

答えて

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

あなたは書くことができます:あなたがsequenできる

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) 

そして今を(あなたが適切なインスタンスを持っていると仮定して)


アップデート:私はここに両方の​​ケースで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))(optionFolder) ^ –

+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インスタンスを持っているものを使用することができます。

関連する問題