I have a situation where I want to abstract over arity, and establish type agreement between one or more "raw" types (A and B below), a method that should return a corresponding Seq(Option[A], Option[B], ...) of those types (named extract below), and a set of field configurations (named configs below), each of which knows how to get a value of the corresponding "raw" type.
In the code below, ideally I'd like Dimensions1 and Dimension2 to not exist. If I had to do some kind of s.c.i.List-like recursive head/tail construct, I would be OK with that. :)
/***
scalaVersion := "2.11.4"
libraryDependencies := Seq("com.chuusai" %% "shapeless" % "2.0.0")
*/
object Main extends App {
import shapeless._
case class JsonEventRecord()
case class DimensionConfig[T](name: String,
valueSource: JsonEventRecord => Option[T]) {
def extract(rec: JsonEventRecord): Option[T] = {
valueSource(rec)
}
}
trait Dimensions {
type All <: HList // I'd like to constrain this to (Option[A], Option[B], ...)
type Configs <: HList // I'd like to constrain this to (DimensionConfig[A], DimensionConfig[B], ...)
def configs: Configs
def extractAll(rec: JsonEventRecord): All
}
// I'd like this to not exist :)
trait Dimensions1 extends Dimensions {
type A
type All = Option[A] :: HNil
type Configs = DimensionConfig[A] :: HNil
val config1: DimensionConfig[A]
def configs = config1 :: HNil
override def extractAll(rec: JsonEventRecord): All = HList(config1.extract(rec))
}
// I'd like this to not exist :)
trait Dimensions2 extends Dimensions {
type A
type B
type All = Option[A] :: Option[B] :: HNil
type Configs = DimensionConfig[A] :: DimensionConfig[B] :: HNil
val config1: DimensionConfig[A]
val config2: DimensionConfig[B]
def configs = config1 :: config2 :: HNil
override def extractAll(rec: JsonEventRecord): All = {
HList(
config1.extract(rec),
config2.extract(rec)
)
}
}
}
If I understand the problem well, you would like a function that given an
HListofDimensionConfig[T]would give you aDimensionwith the type parametersAllandConfigsset to the right types and an implementation ofextractAll. (Correct me if I'm wrong :-)So a dependent function à la shapeless should be able to provide that:
The above defines a "pseudo" dependent function, that accepts a
HListas argument (we will later make it accept only HLists made ofDimensionConfig[T]), and returns aDimensionswith its type parameters set (see below for the definition ofDimensions.Aux- we will makeAllbe a HList made ofOption[T]matching the input HList types).We then have to provide a definition of this dependent function at some values, here HLists made of
DimensionsConfig[T]- this is typically made in the dependent function trait's singleton object:Here we defined
DimensionsOfatHNil, and HLists of the formDimensionConfig[H] :: CT, whereCTis an HList at whichDimensionsOfis it-self defined (as we require - and then use - an implicit that attests so).Dimensions.Auxused in the above definitions is defined likeThe
dimensionsfunction defined above can then be used this way:Dimensions1andDimensions2are not needed any more! And you can getDimensionsfor arbitrary arities at the same time, e.g.: