我尝试构建一个库,允许使用最少量的样板文件来表示相同数据结构的不同表示。
我的想法是使用shapeless.HList
描述所需的表示,将其与实际数据结构匹配,然后使用它来生成所需的表示。
因此,第一步是验证以某种方式描述表示的HList
的类型" align"用数据结构的类型来表示。
我不确定无形状是否提供开箱即用的功能,因此我使用TypeAlignement
推出了下面的天真shapeless.Generic
实现。
import shapeless._
object definition {
case class Property[A](a: A)
case class Embed[A](a: A)
case class Hidden[A](a: A)
def Prop[X] = Property[X] _
def Emb[X] = Embed[X] _
def Hid[X] = Hidden[X] _
trait Representation[A] {
type Repr <: HList
val repr: Repr
}
object Representation {
type Aux[A, R] = Representation[A]{ type Repr = R }
def apply[A, R <: HList](genA: Generic[A], r: R)(implicit typeAlignment: TypeAlignment[genA.Repr, R]): Aux[A, R] = new Representation[A] {
type Repr = R
val repr = r
}
}
case class TypeAlignment[A, B]()
object TypeAlignment {
implicit def hnilTypeAlign2: TypeAlignment[HNil, HNil] = new TypeAlignment[HNil, HNil]()
implicit def propertyRightAlign[A]: TypeAlignment[A, A => Property[A]] = new TypeAlignment[A, A => Property[A]]()
implicit def embedRightAlign[A]: TypeAlignment[A, A => Embed[A]] = new TypeAlignment[A, A => Embed[A]]()
implicit def hiddenRightAlign[A]: TypeAlignment[A, A => Hidden[A]] = new TypeAlignment[A, A => Hidden[A]]()
implicit def hlistTypeAlign[A, B, TA <: HList, TB <: HList](implicit headAlignment: TypeAlignment[A, B], tailAlignment: TypeAlignment[TA, TB]): TypeAlignment[A :: TA, B :: TB] = new TypeAlignment[A :: TA, B :: TB]()
}
}
object demo {
import definition._
type ACL = String
case class Address(city: String, street: String, zipcode: String)
case class User(name: String, age: Option[Int], address: Address, acl : ACL)
val userRepr = Prop[String] :: Prop[Option[Int]] :: Emb[Address] :: Hid[ACL] :: HNil
val bogusRepr = Prop[String] :: Prop[Int] :: Emb[Address] :: Hid[ACL] :: HNil
/*
def makeUserRepr(implicit gen: Generic[User]) = {
val genUser = Generic[User]
Representation(genUser, userRepr)
}
*/
}
当我在scala REPL中尝试时,一切似乎都正常工作:
import shapeless._
import definition._
import demo._
user: demo.User = User(Homer Simpson,Some(42),Address(Springfield,Evergreen terrace,????),admin)
genUser: shapeless.Generic[demo.User]{type Repr = shapeless.::[String,shapeless.::[Option[Int],shapeless.::[demo.Address,shapeless.::[String,shapeless.HNil]]]]} = fresh$macro$5$1@85e91e7
scala> Representation(genUser, bogusRepr)
<console>:18: error: could not find implicit value for parameter typeAlignment: definition.TypeAlignment[genUser.Repr,shapeless.::[String => definition.Property[String],shapeless.::[Int => definition.Property[Int],shapeless.::[demo.Address => definition.Embed[demo.Address],shapeless.::[demo.ACL => definition.Hidden[demo.ACL],shapeless.HNil]]]]]
Representation(genUser, bogusRepr)
^
scala> Representation(genUser, userRepr)
res1: definition.Representation.Aux[demo.User,shapeless.::[String => definition.Property[String],shapeless.::[Option[Int] => definition.Property[Option[Int]],shapeless.::[demo.Address => definition.Embed[demo.Address],shapeless.::[demo.ACL => definition.Hidden[demo.ACL],shapeless.HNil]]]]] = definition$Representation$$anon$1@72bc8c13
当我尝试将User
与bogusRepr
匹配时,我得到令人满意的编译错误,当我使用正确的Representation
时,我得到了userRepr
个实例(并作为奖励,我明白发生了什么的好感觉。
当我尝试取消评论makeUserRepr
中的demo
功能时,问题就开始了。我得到以下编译错误:could not find implicit value for parameter typeAlignment: definition.TypeAlignment[genUser.Repr,shapeless.::[String => definition.Property[String],shapeless.::[Option[Int] => definition.Property[Option[Int]],shapeless.::[demo.Address => definition.Embed[demo.Address],shapeless.::[demo.ACL => definition.Hidden[demo.ACL],shapeless.HNil]]]]]
意味着scalac无法在此上下文中推断出TypeAlignment。
我不明白为什么会这样。毕竟,在REPL会话中正确推断出这里隐含的缺失。因此,我推断我并不真正理解发生了什么......
答案 0 :(得分:0)
好的,我很傻。那些隐含的内容不会从空中出现,所以上面的makeRepresentation
方法应该通过隐含参数来获得所需的TypeAlignment
,这可能很难表达(因为它取决于Generic[A].Repr
)
进一步研究无形,我发现了ZipApply hlist操作,它完全符合我的需要。
object definition {
sealed trait Wrapper[+A]
case class Property[+A](a: A) extends Wrapper[A]
case class Embed[+A](a: A) extends Wrapper[A]
case class Hidden[+A](a: A) extends Wrapper[A]
def Prop[X] = Property[X] _
def Emb[X] = Embed[X] _
def Hid[X] = Hidden[X] _
trait Representation {
type Repr <: HList
val repr: Repr
def expand[A](a: A)(implicit generic: Generic[A]): generic.Repr = generic.to(a)
def represent[L <: HList](a: L)(implicit zipApply: ZipApply[Repr, L]) = repr.zipApply(a)
}
object Representation {
type Aux[R] = Representation{ type Repr = R }
def apply[R <: HList](r: R): Aux[ R] = new Representation {
type Repr = R
val repr = r
}
}
}
我仍然想在一个函数中合并expand
和represent
,但这将是另一个问题。