长话短说,我试图弄清楚如何定义从通用输入到单一类型输出的函数。
背景:这是Mapping over Shapeless record的延续。在Travis's excellent answer之后,我现在有以下内容:
import shapeless._
import poly._
import syntax.singleton._
import record._
type QueryParams = Map[String, Seq[String]]
trait RequestParam[T] {
def value: T
/** Convert value back to a query parameter representation */
def toQueryParams: Seq[(String, String)]
/** Mark this parameter for auto-propagation in new URLs */
def propagate: Boolean
protected def queryStringPresent(qs: String, allParams: QueryParams): Boolean = allParams.get(qs).nonEmpty
}
type RequestParamBuilder[T] = QueryParams => RequestParam[T]
def booleanRequestParam(paramName: String, willPropagate: Boolean): RequestParamBuilder[Boolean] = { params =>
new RequestParam[Boolean] {
def propagate: Boolean = willPropagate
def value: Boolean = queryStringPresent(paramName, params)
def toQueryParams: Seq[(String, String)] = Seq(paramName -> "true").filter(_ => value)
}
}
def stringRequestParam(paramName: String, willPropagate: Boolean): RequestParamBuilder[Option[String]] = { params =>
new RequestParam[Option[String]] {
def propagate: Boolean = willPropagate
def value: Option[String] = params.get(paramName).flatMap(_.headOption)
def toQueryParams: Seq[(String, String)] = value.map(paramName -> _).toSeq
}
}
实际上,以下是一个类构造函数,它将查询字符串中的Map读取作为参数,但为了简单起见,我只是定义了val
:< / p>
val requestParams = Map("no_ads" -> Seq("true"), "edition" -> Seq("us"))
// In reality, there are many more possible parameters, but this is simplified
val options = ('adsDebug ->> booleanRequestParam("ads_debug", true)) ::
('hideAds ->> booleanRequestParam("no_ads", true)) ::
('edition ->> stringRequestParam("edition", false)) ::
HNil
object bind extends FieldPoly {
implicit def rpb[T, K](implicit witness: Witness.Aux[K]): Case.Aux[
FieldType[K, RequestParamBuilder[T]],
FieldType[K, RequestParam[T]]
] = atField(witness)(_(requestParams))
}
// Create queryable option values record by binding the request parameters
val boundOptions = options.map(bind)
这让我这样做:
boundOptions.get('hideAds).value // -> true
问题:现在我希望能够重新序列化propagate = true
的选项。所以基本上,我需要在每个成员的HList
字段上过滤我的propagate
,该字段应始终返回Boolean
,然后让每个参数将自身重新序列化为Seq[(String, String)]
。我尝试过以下方法:
object propagateFilter extends (RequestParam ~> Const[Boolean]) {
override def apply[T](r: RequestParam[T]): Boolean = r.propagate
}
object unbind extends (RequestParam ~> Const[Seq[(String, String)]]) {
override def apply[T](r: RequestParam[T]): Seq[(String, String)] = r.toQueryParams
}
// Reserialize a query string for options that should be propagated
val propagatedParams = boundOptions.values.filter(propagateFilter).map(unbind).toList
// (followed by conventional collections methods)
,但它不喜欢我的功能。我收到以下错误:
<console>:31: error: type mismatch;
found : Boolean
required: shapeless.Const[T]
(which expands to) AnyRef{type λ[T] = T}
override def apply[T](r: RequestParam[T]) = r.propagate
我相信我对一个应该具有多态输入但是单态输出的函数采取了错误的方法。
其他尝试失败:
object propagateFilter extends Poly1 {
implicit def default[T](implicit st: Case.Aux[RequestParam[T], Boolean]) = at[RequestParam[T]](_.propagate)
}
和
def propagate[T](x: RequestParam[T]): Boolean = x.propagate
和
object propagateFilter extends Poly1 {
implicit def default = at[RequestParam[_]](_.propagate)
}
和
object propagateFilter extends FieldPoly {
implicit def rpb[T, K](implicit witness: Witness.Aux[K]): Case.Aux[
FieldType[K, RequestParam[T]],
Boolean
] = atField(witness)(_.propagate)
}
这些都不起作用,可能是由于我自己对正在发生的事情的误解。