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-level 中效仿zip
,所以我们需要单独检查相等的长度。
- 调用站点必须这样做有点难过,否则找不到
import nat._
隐式实例。Length
人们希望在定义站点处理这些细节。(正在等待修复。)
- 如果我理解正确,我不能使用
Mapped
(a la https://stackoverflow.com/a/21005225/86485)来避免长度检查,因为我的一些检查器(例如IsString
)具有比只是更具体的单例类型例如Pred[String]
。
- Travis 指出,它
Pred
可以扩展T => Boolean
,使其可以使用ZipApply
。我把这个建议作为练习留给读者:-)