为什么/如何将类型构造逻辑分成不同的类?
Why / How to separate type construction logic into different classes?
在 中,我注意到需要使用两个 class 构造函数将逻辑分成两个步骤,但尚未完全理解其背后的基本原理。但是我在这里遇到了一个更复杂的问题。
在这种情况下,我需要实现以下目标
case class RawReq(ip: String, header: Map[String, String] = Map())
case class Result(status: String, body: Option[String] = None)
type Directive[T] = T ⇒ Result
class ActionConstructor[T] {
def apply[ExtractedRepr <: HList, ExtraInputRepr <: HList]
(extractor: RawReq ⇒ ExtractedRepr, dir: Directive[T])
(implicit supplement: Supplement.Aux[T, ExtractedRepr, ExtraInputRepr])
: ExtraInputRepr => RawReq => Result
= ???
}
// The Supplement is something to be implemented or replaced ( it should
// generate a ExtraInputRepr type that basically provide the missing
// fields that ExtractedRepr doesn't provide for creating a T, the
// example below explains it better.
// Example usage below
case class RequestMessage(name: String, ipAllowed: Boolean, userId: String)
val dir : Directive[RequestMessage] = (rm: RequestMessage) ⇒
if(rm.ipAllowed)
Result("200", Some("hello! " + rm.name ))
else Result("401")
// the extractor can get one piece of information from the RawReq
val extractor = (r: RawReq) => ('ipAllowed ->> (r.ip.startWith("10.4")> 3)) :: HNil
val ac = new ActionConstructor[RequestMessage]
val action = ac(extractor, dir)
// The other two fields of RequestMessage are provided throw method call
val result = action(('name ->> "Mike") :: ('userId ->> "aId") :: HNil)(RawReq(ip = "10.4.2.5"))
result == Result("200", Some("hello! Mike"))
我的几次尝试都以失败告终。这是最后一个
class PartialHandlerConstructor[T, Repr <: HList, ExtractedRepr <: HList]
(extractor: RawReq ⇒ ExtractedRepr)
(implicit lgen: LabelledGeneric.Aux[T, Repr]) {
def apply[TempFull <: HList, InputRepr <: HList]
(dir: Directive[T])
(implicit removeAll: RemoveAll.Aux[Repr, ExtractedRepr, InputRepr],
prepend: Prepend.Aux[InputRepr, ExtractedRepr, TempFull],
align: Align[TempFull, Repr]): InputRepr ⇒ RawReq ⇒ Result =
(inputRepr: InputRepr) ⇒ (raw: RawReq) ⇒ {
dir(lgen.from(align(inputRepr ++ extractor(raw))))
}
}
class HandlerConstructor[T]() {
def apply[ExtractedRepr <: HList, Repr <: HList]
(extractor: RawReq ⇒ ExtractedRepr)
(implicit lgen: LabelledGeneric.Aux[T, Repr]) = {
new PartialHandlerConstructor[T, Repr, ExtractedRepr](extractor)
}
}
val hc = new HandlerConstructor[RequestMessage]
val handler1 = hc((r: RawReq) ⇒ ('ipAllowed ->> (r.ip.length > 3)) :: HNil)
val dir: Directive[RequestMessage] = (rm: RequestMessage) ⇒ Result(rm.ipAllowed.toString, rm.name)
val action = handler1(dir) // <=== compiler fail
val result = action(('name ->> "big") :: ('userId ->> "aId") :: HNil)(RawReq("anewiP"))
我收到的编译器错误消息位于行 p(dir)
Error:(85, 26) could not find implicit value for parameter removeAll:
shapeless.ops.hlist.RemoveAll.Aux[this.Out,shapeless.::[Boolean with
shapeless.labelled.KeyTag[Symbol with
shapeless.tag.Tagged[String("ipAllowed")],Boolean],shapeless.HNil],InputRepr]
val action = handler1(dir)
^
我认为我失败的根本原因是我不明白为什么以及如何将这种类型构造逻辑组织到不同的 classes 中。而且我不太明白为什么编译器在某些情况下可以找到隐式而在其他情况下却不能。
想出了解决办法。我必须创建自己的 TypeClass RestOf
package com.iheart.playSwagger
import org.specs2.mutable.Specification
import shapeless._
import ops.hlist._
import syntax.singleton._
object Test {
case class RawReq(ip: String, header: Map[String, String] = Map())
case class Result(status: String, body: String)
type Directive[T] = T ⇒ Result
case class RequestMessage(name: String, ipAllowed: Boolean, userId: String)
trait RestOf[L <: HList, SL <: HList] {
type Out <: HList
}
object RestOf {
type Aux[L <: HList, SL <: HList, Out0 <: HList] = RestOf[L, SL] {
type Out = Out0
}
implicit def hlistRestOfNil[L <: HList]: Aux[L, HNil, L] = new RestOf[L, HNil] { type Out = L }
implicit def hlistRestOf[L <: HList, E, RemE <: HList, Rem <: HList, SLT <: HList]
(implicit rt: Remove.Aux[L, E, (E, RemE)], st: Aux[RemE, SLT, Rem]): Aux[L, E :: SLT, Rem] =
new RestOf[L, E :: SLT] { type Out = Rem }
}
class PartialHandlerConstructor[T, Repr <: HList, ExtractedRepr <: HList, InputRepr <: HList]
(extractor: RawReq ⇒ ExtractedRepr)
(implicit lgen: LabelledGeneric.Aux[T, Repr]) {
def apply[TempFull <: HList](dir: Directive[T])
(implicit prepend: Prepend.Aux[InputRepr, ExtractedRepr, TempFull],
align: Align[TempFull, Repr]): InputRepr ⇒ RawReq ⇒ Result =
(inputRepr: InputRepr) ⇒ (raw: RawReq) ⇒ {
dir(lgen.from(align(inputRepr ++ extractor(raw))))
}
}
class HandlerConstructor[T]() {
def apply[Repr <: HList,ExtractedRepr <: HList, InputRepr <: HList]
(extractor: RawReq ⇒ ExtractedRepr)
(implicit lgen: LabelledGeneric.Aux[T, Repr],
restOf: RestOf.Aux[Repr, ExtractedRepr, InputRepr]) = {
new PartialHandlerConstructor[T, Repr, ExtractedRepr, InputRepr](extractor)
}
}
}
class HandlerSpec extends Specification {
import Test._
"handler" should {
"generate action functions" in {
val hc = new HandlerConstructor[RequestMessage]().apply
val handler1 = hc((r: RawReq) ⇒ ('ipAllowed ->> (r.ip.length > 3)) :: HNil)
val dir: Directive[RequestMessage] =
(rm: RequestMessage) ⇒ Result(rm.ipAllowed.toString, rm.name)
val action = handler1(dir)
val result = action(('name ->> "big") :: ('userId ->> "aId") :: HNil)(RawReq("anewiP"))
result === Result("true", "big")
}
}
}
在
在这种情况下,我需要实现以下目标
case class RawReq(ip: String, header: Map[String, String] = Map())
case class Result(status: String, body: Option[String] = None)
type Directive[T] = T ⇒ Result
class ActionConstructor[T] {
def apply[ExtractedRepr <: HList, ExtraInputRepr <: HList]
(extractor: RawReq ⇒ ExtractedRepr, dir: Directive[T])
(implicit supplement: Supplement.Aux[T, ExtractedRepr, ExtraInputRepr])
: ExtraInputRepr => RawReq => Result
= ???
}
// The Supplement is something to be implemented or replaced ( it should
// generate a ExtraInputRepr type that basically provide the missing
// fields that ExtractedRepr doesn't provide for creating a T, the
// example below explains it better.
// Example usage below
case class RequestMessage(name: String, ipAllowed: Boolean, userId: String)
val dir : Directive[RequestMessage] = (rm: RequestMessage) ⇒
if(rm.ipAllowed)
Result("200", Some("hello! " + rm.name ))
else Result("401")
// the extractor can get one piece of information from the RawReq
val extractor = (r: RawReq) => ('ipAllowed ->> (r.ip.startWith("10.4")> 3)) :: HNil
val ac = new ActionConstructor[RequestMessage]
val action = ac(extractor, dir)
// The other two fields of RequestMessage are provided throw method call
val result = action(('name ->> "Mike") :: ('userId ->> "aId") :: HNil)(RawReq(ip = "10.4.2.5"))
result == Result("200", Some("hello! Mike"))
我的几次尝试都以失败告终。这是最后一个
class PartialHandlerConstructor[T, Repr <: HList, ExtractedRepr <: HList]
(extractor: RawReq ⇒ ExtractedRepr)
(implicit lgen: LabelledGeneric.Aux[T, Repr]) {
def apply[TempFull <: HList, InputRepr <: HList]
(dir: Directive[T])
(implicit removeAll: RemoveAll.Aux[Repr, ExtractedRepr, InputRepr],
prepend: Prepend.Aux[InputRepr, ExtractedRepr, TempFull],
align: Align[TempFull, Repr]): InputRepr ⇒ RawReq ⇒ Result =
(inputRepr: InputRepr) ⇒ (raw: RawReq) ⇒ {
dir(lgen.from(align(inputRepr ++ extractor(raw))))
}
}
class HandlerConstructor[T]() {
def apply[ExtractedRepr <: HList, Repr <: HList]
(extractor: RawReq ⇒ ExtractedRepr)
(implicit lgen: LabelledGeneric.Aux[T, Repr]) = {
new PartialHandlerConstructor[T, Repr, ExtractedRepr](extractor)
}
}
val hc = new HandlerConstructor[RequestMessage]
val handler1 = hc((r: RawReq) ⇒ ('ipAllowed ->> (r.ip.length > 3)) :: HNil)
val dir: Directive[RequestMessage] = (rm: RequestMessage) ⇒ Result(rm.ipAllowed.toString, rm.name)
val action = handler1(dir) // <=== compiler fail
val result = action(('name ->> "big") :: ('userId ->> "aId") :: HNil)(RawReq("anewiP"))
我收到的编译器错误消息位于行 p(dir)
Error:(85, 26) could not find implicit value for parameter removeAll: shapeless.ops.hlist.RemoveAll.Aux[this.Out,shapeless.::[Boolean with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("ipAllowed")],Boolean],shapeless.HNil],InputRepr] val action = handler1(dir) ^
我认为我失败的根本原因是我不明白为什么以及如何将这种类型构造逻辑组织到不同的 classes 中。而且我不太明白为什么编译器在某些情况下可以找到隐式而在其他情况下却不能。
想出了解决办法。我必须创建自己的 TypeClass RestOf
package com.iheart.playSwagger
import org.specs2.mutable.Specification
import shapeless._
import ops.hlist._
import syntax.singleton._
object Test {
case class RawReq(ip: String, header: Map[String, String] = Map())
case class Result(status: String, body: String)
type Directive[T] = T ⇒ Result
case class RequestMessage(name: String, ipAllowed: Boolean, userId: String)
trait RestOf[L <: HList, SL <: HList] {
type Out <: HList
}
object RestOf {
type Aux[L <: HList, SL <: HList, Out0 <: HList] = RestOf[L, SL] {
type Out = Out0
}
implicit def hlistRestOfNil[L <: HList]: Aux[L, HNil, L] = new RestOf[L, HNil] { type Out = L }
implicit def hlistRestOf[L <: HList, E, RemE <: HList, Rem <: HList, SLT <: HList]
(implicit rt: Remove.Aux[L, E, (E, RemE)], st: Aux[RemE, SLT, Rem]): Aux[L, E :: SLT, Rem] =
new RestOf[L, E :: SLT] { type Out = Rem }
}
class PartialHandlerConstructor[T, Repr <: HList, ExtractedRepr <: HList, InputRepr <: HList]
(extractor: RawReq ⇒ ExtractedRepr)
(implicit lgen: LabelledGeneric.Aux[T, Repr]) {
def apply[TempFull <: HList](dir: Directive[T])
(implicit prepend: Prepend.Aux[InputRepr, ExtractedRepr, TempFull],
align: Align[TempFull, Repr]): InputRepr ⇒ RawReq ⇒ Result =
(inputRepr: InputRepr) ⇒ (raw: RawReq) ⇒ {
dir(lgen.from(align(inputRepr ++ extractor(raw))))
}
}
class HandlerConstructor[T]() {
def apply[Repr <: HList,ExtractedRepr <: HList, InputRepr <: HList]
(extractor: RawReq ⇒ ExtractedRepr)
(implicit lgen: LabelledGeneric.Aux[T, Repr],
restOf: RestOf.Aux[Repr, ExtractedRepr, InputRepr]) = {
new PartialHandlerConstructor[T, Repr, ExtractedRepr, InputRepr](extractor)
}
}
}
class HandlerSpec extends Specification {
import Test._
"handler" should {
"generate action functions" in {
val hc = new HandlerConstructor[RequestMessage]().apply
val handler1 = hc((r: RawReq) ⇒ ('ipAllowed ->> (r.ip.length > 3)) :: HNil)
val dir: Directive[RequestMessage] =
(rm: RequestMessage) ⇒ Result(rm.ipAllowed.toString, rm.name)
val action = handler1(dir)
val result = action(('name ->> "big") :: ('userId ->> "aId") :: HNil)(RawReq("anewiP"))
result === Result("true", "big")
}
}
}