Build Shapeless implicit in the function - scala

I have a use case where I create an HList Record and pass it to a different function, and select an element in the function it was passed to. I couldn't find a way to make any of the following work:
Pass an HList to the function with a Witness defined in the function:
def sel0[L <: HList](hl: L) = {
val w = Witness('field1)
implicit val selector = the[Selector[L, w.T]]
selector(hl)
}
or pass and HList and a Witness and build the Selector in the function
def sel1[L <: HList](hl: L, w: Witness) = {
implicit val selector = the[Selector[L, w.T]]
selector(hl)
}
Not even a simpler version where the implicit in contructed from the Witness before sent to the function:
val hl = 'field1 ->> 1 :: 'field2 ->> "2" :: HNil
val w = Witness('field1)
val selector = the[Selector[hl.type, w.T]]
def sel2[L <: HList](hl: L, w: Witness)(implicit selector: Selector[L, w.T]) = {
selector(hl)
}
sel2(r1, w)(selector)
Any suggestion why these above don't work?

Use this one
def sel0[L <: HList](hl: L)(implicit
selector: shapeless.ops.hlist.Selector[L, FieldType[Witness.`'field1`.T, Int]]) =
selector(hl)
or this
def sel0[L <: HList](hl: L)(implicit
selector: shapeless.ops.record.Selector[L, Witness.`'field1`.T]) =
selector(hl)
selector(selector) is very strange.
You don't need hl.type. It's the type of this particular hl. The correct type of hl is the type of all such hls, i.e. Record.`'field1 -> Int, 'field2 -> String`.T or FieldType[Witness.`'field1`.T, Int] :: FieldType[Witness.`'field2`.T, String] :: HNil.

Related

Avoid losing type info in return value

I'm trying to find a way to avoid losing type information in the return value for my method.
I have the following:
val defs0 = Default.mkDefault[Person, Some[String] :: Some[Int] :: HNil](Some("odd") :: Some(42) :: HNil)
Using IntelliJs "Add type annotation" gives the type:
Default.Aux[Person, ::[Some[String], ::[Some[Int], HNil]]]
This is fine, except I dont want to specify the fields of Person when I call mkDefault. So I created this:
object MkDefault {
object toSome extends Poly1 {
implicit def default[P] = at[P](Some(_))
}
def apply[P, L <: HList, D <: HList]
(p: P)
(implicit
lg: LabelledGeneric.Aux[P, L],
mpr: Mapper.Aux[toSome.type, L, D]
): Default.Aux[P, D] =
Default.mkDefault[P, D](mpr(lg.to(p)))
}
Now I can do:
val defs1 = MkDefault(Person("odd", 42))
Which is good, except the inferred type from IntellJ looks like this:
Default.Aux[Person, HNil]
How can I make the inferred type of defs1 equal to the inferred type of defs0?
*without having to specify the fields of class Person
Use mpr.Out instead of D as an output type:
object MkDefault {
object toSome extends Poly1 {
implicit def default[P] = at[P](Some(_))
}
def apply[P, L <: HList, D <: HList]
(p: P)
(implicit
lg: LabelledGeneric.Aux[P, L],
mpr: Mapper.Aux[toSome.type, L, D]
): Default.Aux[P, mpr.Out] =
Default.mkDefault[P, D](mpr(lg.to(p)))
}
Example:
scala> val defs1 = MkDefault(Person("odd", 42))
defs1: shapeless.Default[Person]{type Out = shapeless.::[Some[String with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("name")],String]],shapeless.::[Some[Int with shapeless.labelled.KeyTag[Symbol with shapeless.tag.Tagged[String("age")],Int]],shapeless.HNil]]} = shapeless.Default$$anon$1#1f6a8bb8
P.S.
If you don't mind Option instead of Some, you can also use AsOptions
val opt = Default.AsOptions[Person]
val def3 = Default.mkDefault[Person, opt.Out](Some("aaa") :: Some(5) :: HNil)
Check:
scala> implicitly[def3.Out =:= ::[Option[String], ::[Option[Int], HNil]]]
res12: =:=[def3.Out,shapeless.::[Option[String],shapeless.::[Option[Int],shapeless.HNil]]] = <function1>

Map for generic HList

Say we have following method
def func[T <: HList](hlist: T, poly: Poly)
(implicit mapper : Mapper[poly.type, T]): Unit = {
hlist map poly
}
and custom Poly
object f extends (Set ~>> String) {
def apply[T](s : Set[T]) = s.head.toString
}
So I can use this func like
func(Set(1, 2) :: Set(3, 4) :: HNil, f)
In my code I have small number of Polies and a big number of func invocations. For this purpose I tried to move poly: Poly to implicit parameters and got expected message
illegal dependent method type: parameter appears in the type of another parameter in the same section or an earlier one
How could I change or extend poly: Poly parameter to avoid this error (I need to keep type signature func[T <: HList](...))?
Maybe you could use the "partially applied" trick using a class with an apply method :
import shapeless._
import ops.hlist.Mapper
final class PartFunc[P <: Poly](val poly: P) {
def apply[L <: HList](l: L)(implicit mapper: Mapper[poly.type, L]): mapper.Out =
l map poly
}
def func[P <: Poly](poly: P) = new PartFunc(poly)
With your poly f :
val ff = func(f)
ff(Set(1, 2) :: Set(3, 4) :: HNil) // 1 :: 3 :: HNil
ff(Set("a", "b") :: Set("c", "d") :: HNil) // a :: c :: HNil

Shapeless HList map after foldRight

A type level foldRight works fine (getLabelWithValues), and a follow-on type level map (getValues) also works fine. If I combine both in one method (getValuesFull), it doesn't work any more though. What is the missing piece?
The full source (with sbt ready to ~run with implicit debug output) is here: https://github.com/mpollmeier/shapeless-playground/tree/8170a5b
case class Label[A](name: String)
case class LabelWithValue[A](label: Label[A], value: A)
val label1 = Label[Int]("a")
val labels = label1 :: HNil
object combineLabelWithValue extends Poly2 {
implicit def atLabel[A, B <: HList] = at[Label[A], (B, Map[String, Any])] {
case (label, (acc, values)) ⇒
(LabelWithValue(label, values(label.name).asInstanceOf[A]) :: acc, values)
}
}
object GetLabelValue extends (LabelWithValue ~> Id) {
def apply[B](labelWithValue: LabelWithValue[B]) = labelWithValue.value
}
val labelsWithValues: LabelWithValue[Int] :: HNil = getLabelWithValues(labels)
// manually mapping it works fine:
val valuesManual: Int :: HNil = labelsWithValues.map(GetLabelValue)
// using a second function with Mapper works fine:
val valuesSecondFn: Int :: HNil = getValues(labelsWithValues)
// error: could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper.Aux[Main.GetLabelValue.type,WithValues,Values]
// val valuesFull: Int :: HNil = getValuesFull(labels)
def getLabelWithValues[L <: HList, P, WithValues](labels: L)(
implicit folder: RightFolder.Aux[L, (HNil.type, Map[String, Any]), combineLabelWithValue.type, P],
ic: IsComposite.Aux[P, WithValues, _]
): WithValues = {
val state = Map("a" -> 5, "b" -> "five")
val resultTuple = labels.foldRight((HNil, state))(combineLabelWithValue)
ic.head(resultTuple)
}
def getValues[WithValues <: HList, Values <: HList](withValues: WithValues)(
implicit mapper: Mapper.Aux[GetLabelValue.type, WithValues, Values]
): Values = {
withValues.map(GetLabelValue)
}
def getValuesFull[L <: HList, P, WithValues <: HList, Values <: HList](labels: L)(
implicit folder: RightFolder.Aux[L, (HNil.type, Map[String, Any]), combineLabelWithValue.type, P],
ic: IsComposite.Aux[P, WithValues, _],
mapper: Mapper.Aux[GetLabelValue.type, WithValues, Values]
): Values = {
val state = Map("a" -> 5, "b" -> "five")
val resultTuple = labels.foldRight((HNil, state))(combineLabelWithValue)
val withValues: WithValues = ic.head(resultTuple)
withValues.map(GetLabelValue)
}
The issue here is that you're ending up trying to map over an HList where the HNil is statically typed as HNil.type. This doesn't work in general—e.g. in a simplified case like this:
import shapeless._, ops.hlist.Mapper
val mapped1 = Mapper[poly.identity.type, HNil]
val mapped2 = Mapper[poly.identity.type, HNil.type]
mapped1 will compile, but mapped2 won't.
The trick is to change the HNil.type in your RightFolder types to HNil and then to call foldRight with HNil: HNil. This will make everything work just fine.
There are a few other suggestions I'd make (destructure the tuple in place of P instead of using IsComposite, skip the Aux on mapper and return mapper.Out instead of having a Value type parameter, etc.), but they're probably out of the scope of this question.

Putting out the Bonfire of the Arities with Shapeless?

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 HList of
DimensionConfig[T] would give you a Dimension with the type parameters All and Configs set to the right types and an implementation of extractAll. (Correct me if I'm wrong :-)
So a dependent function à la shapeless should be able to provide that:
trait DimensionsOf[L <: HList] extends DepFn1[L] {
type All <: HList
type Out = Dimensions.Aux[All, L]
}
def dimensions[L <: HList](configs: L)
(implicit dimensionsOf: DimensionsOf[L]): dimensionsOf.Out =
dimensionsOf(configs)
The above defines a "pseudo" dependent function, that accepts a HList as argument (we will later make it accept only HLists made of DimensionConfig[T]), and returns a Dimensions with its type parameters set (see below for the definition of Dimensions.Aux - we will make All be a HList made of Option[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:
object DimensionsOf {
type Aux[L <: HList, All0 <: HList] = DimensionsOf[L] { type All = All0 }
implicit val dimensionsOfHNil: DimensionsOf.Aux[HNil, HNil] =
new DimensionsOf[HNil] {
type All = HNil
def apply(l: HNil) = new Dimensions {
type All = HNil
type Configs = HNil
val configs = HNil
def extractAll(rec: JsonEventRecord) = HNil
}
}
implicit def dimensionsOfHCons[H, CT <: HList]
(implicit dimensionsOfTail: DimensionsOf[CT]): Aux[DimensionConfig[H] :: CT, Option[H] :: dimensionsOfTail.All] =
new DimensionsOf[DimensionConfig[H] :: CT] {
type All = Option[H] :: dimensionsOfTail.All
def apply(l: DimensionConfig[H] :: CT) = new Dimensions {
type All = Option[H] :: dimensionsOfTail.All
type Configs = DimensionConfig[H] :: CT
val configs = l
val tailDimensions = dimensionsOfTail(l.tail)
def extractAll(rec: JsonEventRecord) = l.head.extract(rec) :: tailDimensions.extractAll(rec)
}
}
}
Here we defined DimensionsOf at HNil, and HLists of the form DimensionConfig[H] :: CT, where CT is an HList at which DimensionsOf is it-self defined (as we require - and then use - an implicit that attests so).
Dimensions.Aux used in the above definitions is defined like
object Dimensions {
type Aux[All0 <: HList, Configs0 <: HList] = Dimensions { type All = All0; type Configs = Configs0 }
}
The dimensions function defined above can then be used this way:
val intConfig = DimensionConfig[Int]("Int", _ => Some(2))
val stringConfig = DimensionConfig[String]("String", _ => Some("a"))
val dimensions1 = dimensions(intConfig :: HNil)
val res1 = dimensions1.extractAll(JsonEventRecord()) // Option[Int] :: HNil
val dimensions2 = dimensions(intConfig :: stringConfig :: HNil)
val res2 = dimensions2.extractAll(JsonEventRecord()) // Option[Int] :: Option[String] :: HNil
Dimensions1 and Dimensions2 are not needed any more! And you can get Dimensions for arbitrary arities at the same time, e.g.:
val dimensions4 = dimensions(intConfig :: stringConfig :: intConfig :: stringConfig :: HNil)
val res4 = dimensions4.extractAll(JsonEventRecord()) // Option[Int] :: Option[String] :: Option[Int] :: Option[String] :: HNil

Passing a Shapeless Extensible Record to a Function (continued)

Considering this question : Passing a Shapeless Extensible Record to a Function, Travis's answer shows that every function taking an extensible record as parameter must have an implicit selector as parameter. I wonder if one could factorize those declarations in case we have many functions of this kind.
E.g. :
val w1 = Witness("foo1")
val w2 = Witness("foo2")
val w3 = Witness("foo3")
//Here some "magical" declarations avoiding to declara selectors in fun1, fun2, fun3 below
def fun1[L <: HList](xs: L) = ... //Access to foo1, foo2, foo3
def fun2[L <: HList](xs: L) = ... //Access to foo1, foo2, foo3
def fun3[L <: HList](xs: L) = ... //Access to foo1, foo2, foo3
Thanks
Benoit
edit on Dec 10
When trying the code of the answer, on comes with two problems :
Nothing is told about the real type of the data associated to foo1, foo2, foo3 : consequently, a function like fun1 can't use any method associated to these types. e.g., even if foo3 is a Double, it can't take its squareroot.
If I call fun1 with ("foo1"->> "hello") :: ("foo2" -> 1)::("foo3" ->> 1.2)::HNiL, the result is (hello, 1, 1.2) with type (selectors.s1.Out, selectors.s2.Out, selectors.s3.Out)
If I try to add 1 to the last value (1.2), Scala complains that it can't add an Int and a selectors.s3.Out ;but if I write :
val x = fun1(("foo1"->> "hello") :: ("foo2" -> 1)::("foo3" ->> 1.2)::HNil)
I can write :
x._3 == 1.2
and scala answers True!
I have tried to modify the code in this way, hopping that the types would be propagated, but it doesn't solve the problem. I can't even call fun1 with (foo1->> "hello") :: (foo2 -> 1)::(foo3 ->> 1.2)::HNil as parameter :
object foo1 extends FieldOf[String]
object foo2 extends FieldOf[Int]
object foo3 extends FieldOf[Double]
val w1 = Witness(foo1)
val w2 = Witness(foo2)
val w3 = Witness(foo3)
case class HasMyFields[L <: HList](implicit
s1: Selector[L, w1.T],
s2: Selector[L, w2.T],
s3: Selector[L, w3.T]
)
object HasMyFields {
implicit def make[L <: HList](implicit
s1: Selector[L, w1.T],
s2: Selector[L, w2.T],
s3: Selector[L, w3.T]
) = HasMyFields[L]
}
def fun1[L <: HList](xs: L)(implicit selectors: HasMyFields[L]) = {
import selectors._
(xs(foo1), xs(foo2), xs(foo3))
}
Is there a way to progress?
Benoit
You can define your own type class to gather the evidence that the record has the fields you need:
import shapeless._, ops.record.Selector, record._, syntax.singleton._
val w1 = Witness("foo1")
val w2 = Witness("foo2")
val w3 = Witness("foo3")
case class HasMyFields[L <: HList](implicit
s1: Selector[L, w1.T, String],
s2: Selector[L, w2.T, Int],
s3: Selector[L, w3.T, Double]
)
object HasMyFields {
implicit def make[L <: HList](implicit
s1: Selector[L, w1.T, String],
s2: Selector[L, w2.T, Int],
s3: Selector[L, w3.T, Double]
) = HasMyFields[L]
}
And then, for example:
def fun1[L <: HList](xs: L)(implicit selectors: HasMyFields[L]) = {
import selectors._
(xs("foo1"), xs("foo2"), xs("foo3"))
}
It's still a little verbose, especially since the import is necessary, but much less so than requiring all of the selectors individually as implicit parameters.
The out type of a given field can be specified using:
Selector[L, w1.T] { type Out = String }
Also, we can slightly simplify the syntax using a type constructor:
import shapeless._, ops.record.Selector, record._, syntax.singleton._
val w1 = Witness("foo1")
val w2 = Witness("foo2")
val w3 = Witness("foo3")
type HasFoo1[L <: HList] = Selector[L, w1.T] { type Out = String }
type HasFoo2[L <: HList] = Selector[L, w2.T]
type HasFoo3[L <: HList] = Selector[L, w3.T]
#implicitNotFound("${L} should have foo1, foo2 and foo3")
case class HasMyFields[L <: HList](implicit s1: HasFoo1[L], s2: HasFoo2[L], s3: HasFoo3[L])
object HasMyFields {
implicit def make[L <: HList : HasFoo1 : HasFoo2 : HasFoo3] = HasMyFields[L]
}
def fun1[L <: HList : HasMyFields](xs: L) = {
val selectors = implicitly[HasMyFields[L]]
import selectors._
(xs("foo1").length, xs("foo2"), xs("foo3"))
}
fun1(("foo1"->> "hello") :: ("foo2" ->> 1)::("foo3" ->> 1.2)::HNil)
// Does not compile: the value in foo1 is not a String
fun1(("foo1"->> 2) :: ("foo2" ->> 1)::("foo3" ->> 1.2)::HNil)