Travis Brown によるこれらの回答には、必要なもののほとんどが含まれています。
しかし、それらの答えを見つけ、それらがあなたの問題に適用可能であることを理解し、それらを組み合わせて適用する詳細を考え出すのに長い時間がかかりました.
そして、あなたの質問は、実際の問題、つまり入力の検証を解決するときにこれがどのように発生するかを示しているため、価値があると思います。また、デモ コードとテストを含む完全なソリューションを示すことで、以下に価値を追加しようとします。
チェックを行うための一般的なコードは次のとおりです。
object Checker {
import shapeless._, poly._, ops.hlist._
object check extends Poly1 {
implicit def apply[T] = at[(T, Pred[T])]{
case (t, pred) => pred(t)
}
}
def apply[L1 <: HList, L2 <: HList, N <: Nat, Z <: HList, M <: HList](input: L1, spec: L2)(
implicit zipper: Zip.Aux[L1 :: L2 :: HNil, Z],
mapper: Mapper.Aux[check.type, Z, M],
length1: Length.Aux[L1, N],
length2: Length.Aux[L2, N],
toList: ToList[M, Boolean]) =
input.zip(spec)
.map(check)
.toList
.forall(Predef.identity)
}
デモの使用コードは次のとおりです。
object Frank {
import shapeless._, nat._
def main(args: Array[String]) {
val IsString = new Pred[String] { def apply(s: String) = true }
val IsOddNumber = new Pred[Int] { def apply(n: Int) = n % 2 != 0 }
val IsEvenNumber = new Pred[Int] { def apply(n: Int) = n % 2 == 0 }
val spec = IsEvenNumber :: IsString :: IsString :: IsOddNumber :: HNil
val goodInput = 4 :: "foo" :: "" :: 5 :: HNil
val badInput = 4 :: "foo" :: "" :: 4 :: HNil
val malformedInput1 = 4 :: 5 :: "" :: 6 :: HNil
val malformedInput2 = 4 :: "foo" :: "" :: HNil
val malformedInput3 = 4 :: "foo" :: "" :: 5 :: 6 :: HNil
println(Checker(goodInput, spec))
println(Checker(badInput, spec))
import shapeless.test.illTyped
illTyped("Checker(malformedInput1, spec)")
illTyped("Checker(malformedInput2, spec)")
illTyped("Checker(malformedInput3, spec)")
}
}
/*
results when run:
[info] Running Frank
true
false
*/
illTyped
を使用して、コンパイルすべきではないコードがコンパイルされないことを確認することに注意してください。
いくつかの補足事項:
- 私は当初、これを使って長い庭の道をたどりました。ここでは、すべての場合の戻り値の型がブール型であることを表すために、多相関数
check
が よりも具体的な型を持つことが重要だと考えました。Poly1
だから私はそれを で動作させようとし続けましたextends (Id ~>> Boolean)
。しかし、結果の型がすべての場合にブール型であることを型システムが認識しているかどうかは問題ではありません。私たちが実際に持っている唯一のケースが正しい型を持っていれば十分です。extends Poly1
は素晴らしいことです。
- 値レベル
zip
は伝統的に、長さが等しくないことを許可し、エクストラを破棄します。Miles は Shapeless の type-levelzip
でこれに続いたので、長さが等しいかどうかを個別にチェックする必要があります。
- 呼び出しサイトが でなければならないのは少し悲しいことです。
import nat._
そうしないと、 の暗黙のインスタンスLength
が見つかりません。これらの詳細は、定義サイトで処理することをお勧めします。(修正は保留中です。 )
- 私が正しく理解していれば、長さチェックを回避するために
Mapped
(a la https://stackoverflow.com/a/21005225/86485IsString
) を使用することはできません。例えばPred[String]
。
Pred
Travis は、それを拡張T => Boolean
して使用できるようにする可能性があると指摘していますZipApply
。読者の演習として、この提案に従っておきます:-)