Shapeless:使用 Coproduct 拥有 HList 约束
Shapeless: own HList constraint using Coproduct
(注意:从 拆分)
问题 2 - 使用余积的自身约束
我真正想做的是使用 Coproduct 编写一个新约束。
trait CPConstraint[L <: HList, CP <: Coproduct] extends Serializable
object CPConstraint {
import shapeless.ops.coproduct.Selector._
def apply[L <: HList, CP <: Coproduct](implicit cpc: CPConstraint[L, CP]): CPConstraint[L, CP] = cpc
type <*<[CP <: Coproduct] = { // TODO: just invented a symbol ... what would be an appropriate one?
type λ[L <: HList] = CPConstraint[L, CP]
}
implicit def hnilCP[HN <: HNil, CP <: Coproduct]: CPConstraint[HN, CP] = new CPConstraint[HN, CP] {}
implicit def hlistCP[H, T <: HList, CP <: Coproduct](implicit ev: coproduct.Selector[CP, H], cpct: CPConstraint[T, CP]): CPConstraint[H :: T, CP] = new CPConstraint[H :: T, CP] {}
}
object testCPConstraint {
import shapeless.ops.coproduct.Selector._
import CPConstraint._
type CPType = Long :+: String :+: CNil
implicit val selLong = implicitly[Selector[CPType, Long]]
implicit val selString = implicitly[Selector[CPType, String]]
def acceptCP[L <: HList : <*<[CPType]#λ](l: L) = true
val hlLong: ::[Long, HNil] = 1L :: HNil
val hlString: ::[String, HNil] = "blabla" :: HNil
val hlMixed: ::[String, ::[Long, HNil]] = "blabla" :: 1L :: HNil
val hlMixedRev: ::[Long, ::[String, HNil]] = 1L :: "blabla" :: HNil
val hlInvalid: ::[Double, HNil] = 1.0d :: HNil
implicit val scpcEmpty: CPConstraint[HNil, CPType] = implicitly[CPConstraint[HNil, CPType]]
implicit val scpcEmptyLong1: CPConstraint[::[Long,HNil], CPType] = new CPConstraint[::[Long,HNil], CPType] {}
// 隐式 val scpcEmptyLong2: CPConstraint[hlLong.type, CPType] = new CPConstraint[hlLong.type, CPType] {}
// 上一行适合缺失的隐式 - 为什么???
implicit val cpcLong = implicitly[CPConstraint[hlLong.type, CPType]]
val validEmpty = acceptCP(HNil: HNil)
val validLong = acceptCP(1l :: HNil)
val validMixed = acceptCP("blabla" :: 1l :: HNil)
val invalid = acceptCP(1.0d :: HNil) // should fail due to missing evidence
}
在试验过程中...我成功了:
import shapeless.ops.coproduct
import shapeless.{:+:, ::, CNil, Coproduct, HList, HNil}
/**
* constraint that checks {{{shapeless.HList}}} elements to be of a type contained in a {{{shapeless.Coproduct}}}
*/
trait CPConstraint[L <: HList, CP <: Coproduct] extends Serializable
object CPConstraint {
def apply[L <: HList, CP <: Coproduct](implicit cpc: CPConstraint[L, CP]): CPConstraint[L, CP] = cpc
type <*<[CP <: Coproduct] = {
type λ[L <: HList] = CPConstraint[L, CP]
}
implicit def hnilCP[HN <: HNil, CP <: Coproduct]: CPConstraint[HN, CP] = new CPConstraint[HN, CP] {}
implicit def hlistCP[H, T <: HList, CP <: Coproduct](implicit ev: coproduct.Selector[CP, H], cpct: CPConstraint[T, CP]): CPConstraint[H :: T, CP] = new CPConstraint[H :: T, CP] {}
}
object testCPConstraint {
import CPConstraint._
type CPType = Long :+: String :+: CNil
def acceptCP[L <: HList : <*<[CPType]#λ](l: L) = true
val hlLong = 1L :: (HNil: HNil)
val hlString = "blabla" :: HNil
val hlMixed = "blabla" :: 1L :: HNil
val hlMixedRev = 1L :: "blabla" :: HNil
val hlInvalid = 1.0d :: HNil
val validEmpty = acceptCP(HNil)
val validEmpty2 = acceptCP(HList())
val validEmpty3 = acceptCP(HNil: HNil)
val validString = acceptCP(hlString)
val validLong = acceptCP(hlLong)
val validMixed = acceptCP(hlMixed)
val validMixedRev = acceptCP(hlMixedRev)
// val invalid = acceptCP(hlInvalid) // -> fails due to missing evidence (as expected)
implicit val scpcEmpty = implicitly[CPConstraint[HNil, CPType]]
// implicit val cpcLong = implicitly[CPConstraint[hlLong.type, CPType]]
// I still don't understand why above line doesn't work, but the following does ???
implicit val cpcLong2 = implicitly[CPConstraint[::[Long,HNil], CPType]]
}
这个答案无耻地复制自gitter,所有功劳归功于@SystemFW。
使用 LiftAll
witness,像这样:
import shapeless._, ops.hlist.LiftAll, ops.coproduct.Inject
type Cop = Int :+: Long :+: CNil
def foo[H <: HList](implicit ev: LiftAll[Inject[Cop,?], H]) = null
foo[Int :: Long :: HNil]
foo[HNil]
foo[Int :: Int :: HNil]
//foo[Int :: Long :: String :: HNil] doesn't compile
其中 Inject[Cop,?]
是 type I[X] = Inject[Cop,X]
(或者您可以使用 kind projector 插件来实现 ?
语法)。
(注意:从
问题 2 - 使用余积的自身约束
我真正想做的是使用 Coproduct 编写一个新约束。
trait CPConstraint[L <: HList, CP <: Coproduct] extends Serializable
object CPConstraint {
import shapeless.ops.coproduct.Selector._
def apply[L <: HList, CP <: Coproduct](implicit cpc: CPConstraint[L, CP]): CPConstraint[L, CP] = cpc
type <*<[CP <: Coproduct] = { // TODO: just invented a symbol ... what would be an appropriate one?
type λ[L <: HList] = CPConstraint[L, CP]
}
implicit def hnilCP[HN <: HNil, CP <: Coproduct]: CPConstraint[HN, CP] = new CPConstraint[HN, CP] {}
implicit def hlistCP[H, T <: HList, CP <: Coproduct](implicit ev: coproduct.Selector[CP, H], cpct: CPConstraint[T, CP]): CPConstraint[H :: T, CP] = new CPConstraint[H :: T, CP] {}
}
object testCPConstraint {
import shapeless.ops.coproduct.Selector._
import CPConstraint._
type CPType = Long :+: String :+: CNil
implicit val selLong = implicitly[Selector[CPType, Long]]
implicit val selString = implicitly[Selector[CPType, String]]
def acceptCP[L <: HList : <*<[CPType]#λ](l: L) = true
val hlLong: ::[Long, HNil] = 1L :: HNil
val hlString: ::[String, HNil] = "blabla" :: HNil
val hlMixed: ::[String, ::[Long, HNil]] = "blabla" :: 1L :: HNil
val hlMixedRev: ::[Long, ::[String, HNil]] = 1L :: "blabla" :: HNil
val hlInvalid: ::[Double, HNil] = 1.0d :: HNil
implicit val scpcEmpty: CPConstraint[HNil, CPType] = implicitly[CPConstraint[HNil, CPType]]
implicit val scpcEmptyLong1: CPConstraint[::[Long,HNil], CPType] = new CPConstraint[::[Long,HNil], CPType] {}
// 隐式 val scpcEmptyLong2: CPConstraint[hlLong.type, CPType] = new CPConstraint[hlLong.type, CPType] {} // 上一行适合缺失的隐式 - 为什么???
implicit val cpcLong = implicitly[CPConstraint[hlLong.type, CPType]]
val validEmpty = acceptCP(HNil: HNil)
val validLong = acceptCP(1l :: HNil)
val validMixed = acceptCP("blabla" :: 1l :: HNil)
val invalid = acceptCP(1.0d :: HNil) // should fail due to missing evidence
}
在试验过程中...我成功了:
import shapeless.ops.coproduct
import shapeless.{:+:, ::, CNil, Coproduct, HList, HNil}
/**
* constraint that checks {{{shapeless.HList}}} elements to be of a type contained in a {{{shapeless.Coproduct}}}
*/
trait CPConstraint[L <: HList, CP <: Coproduct] extends Serializable
object CPConstraint {
def apply[L <: HList, CP <: Coproduct](implicit cpc: CPConstraint[L, CP]): CPConstraint[L, CP] = cpc
type <*<[CP <: Coproduct] = {
type λ[L <: HList] = CPConstraint[L, CP]
}
implicit def hnilCP[HN <: HNil, CP <: Coproduct]: CPConstraint[HN, CP] = new CPConstraint[HN, CP] {}
implicit def hlistCP[H, T <: HList, CP <: Coproduct](implicit ev: coproduct.Selector[CP, H], cpct: CPConstraint[T, CP]): CPConstraint[H :: T, CP] = new CPConstraint[H :: T, CP] {}
}
object testCPConstraint {
import CPConstraint._
type CPType = Long :+: String :+: CNil
def acceptCP[L <: HList : <*<[CPType]#λ](l: L) = true
val hlLong = 1L :: (HNil: HNil)
val hlString = "blabla" :: HNil
val hlMixed = "blabla" :: 1L :: HNil
val hlMixedRev = 1L :: "blabla" :: HNil
val hlInvalid = 1.0d :: HNil
val validEmpty = acceptCP(HNil)
val validEmpty2 = acceptCP(HList())
val validEmpty3 = acceptCP(HNil: HNil)
val validString = acceptCP(hlString)
val validLong = acceptCP(hlLong)
val validMixed = acceptCP(hlMixed)
val validMixedRev = acceptCP(hlMixedRev)
// val invalid = acceptCP(hlInvalid) // -> fails due to missing evidence (as expected)
implicit val scpcEmpty = implicitly[CPConstraint[HNil, CPType]]
// implicit val cpcLong = implicitly[CPConstraint[hlLong.type, CPType]]
// I still don't understand why above line doesn't work, but the following does ???
implicit val cpcLong2 = implicitly[CPConstraint[::[Long,HNil], CPType]]
}
这个答案无耻地复制自gitter,所有功劳归功于@SystemFW。
使用 LiftAll
witness,像这样:
import shapeless._, ops.hlist.LiftAll, ops.coproduct.Inject
type Cop = Int :+: Long :+: CNil
def foo[H <: HList](implicit ev: LiftAll[Inject[Cop,?], H]) = null
foo[Int :: Long :: HNil]
foo[HNil]
foo[Int :: Int :: HNil]
//foo[Int :: Long :: String :: HNil] doesn't compile
其中 Inject[Cop,?]
是 type I[X] = Inject[Cop,X]
(或者您可以使用 kind projector 插件来实现 ?
语法)。