@TravisBrown的问题主要基于他之前的博文here。
由于遗留代码库的原因,我非常痛苦地在这里概述,我需要在构建应用程序链时用字符串键标记验证。为了避免让事情变得过于复杂,我已经将博客文章版本改编为Shapeless 2,但却没有惨淡地将我需要的那些内容简单地分离出来。
我认为潜在的诀窍是简单地重复使用链中的第一个ApplicativeBuilder
,然后解开结果,从(D, (C, (B, A))) => (A, B, C, D)
开始,一次解开一个嵌套。
如何重复使用它来执行以下操作:
package test {
type ErrorsOr[A] = ValidationNel[String, A]
type NestedErrorsOr[T] = ValidationNel[(String, String), T]
type Validator[A] = String => ErrorsOr[A]
implicit class VdNelAug[T](val vd: ErrorsOr[T]) extends AnyVal {
def label(key: String): WrappedValidation[T] = new WrappedValidation[T](key, vd.leftMap(
nel => nel.map(value => key -> value))
)
}
}
class WrappedValidation[T](val prop: String, val validation: NestedErrorsOr[T]) {
def and[A](wv: WrappedValidation[A]): ValidationBuilder[
NestedErrorsOr[T] :: NestedErrorsOr[A] :: HNil,
T :: A :: HNil] = {
// I don't know how to infer the type here.
new ValidationBuilder(
List(wv.prop, prop),
validation |@| wv.validation)
)
}
}
class ValidationBuilder[
ValTypes <: HList,
ArgTypes <: HList,
BuilderType = ???
](builder: BuilderType?): {
def apply[V, Ret, Rev <: Hlist, Args <: HList](fn: V => Ret)(
implicit rev: Reverse.Aux[ArgTypes, Rev]
tupler: Tupler.Aux[Rev, Args],
ev: Args =:= V
): Either[CustomError, Ret] = {
// ingnore the custom conversion of the nel, this is just about
// performing a simple groupBy the label such as all the error messages
// For a given label are returned under the same key.
builder.fold(nel => nel.toCustom, fn)
}
我相信我得到了原则,但差距来自缺乏无形API体验。
object Applier extends Poly2 {
implicit def ap[F[_]: Applicative, H, T <: HList, R]:
Case2.Aux[Applier.type, F[(H :: T) => R], F[H], F[T => R]] =
at[F[(H :: T) => R], F[H]](
(f, fa) => fa <*> f.map(hf => (h: H) => (t: T) => hf(h :: t))
)
}
我猜这个技巧是示例中的poly和文件夹的组合,但是我不明白为什么提供的F[_]
是必要的,因为我们可以预测它的类型通过使用hlist链来应用我们可以从验证本身的类型参数创建。 F[_]
我不是指Poly
中的那个,而是用于推导正确验证的那个。
这个函数不能只是一个In => Out
,其中In
与解包验证的类型相同吗?