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.
Related
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>
Given an HList of Label[A](String) I want to map it into an HList of LabelWithValue[A](Label[A], A), where the actual values come from a Map[String, Any]. In the example below I just defined the map of values in the method, just imagine the values come from a database.
The below works, but it is very veeery hacky because it uses a global var. Instead I'd like to pass the Map[String, Any] into GetLabelWithValue. I didn't find a way though, because the caller of getValues implicitly creates a Mapper, and at that point the map of values doesn't exist yet. I tried to create a Mapper myself, but my type level programming skills aren't yet good enough.
import shapeless._
import shapeless.poly._
import shapeless.ops.hlist._
object Main extends App {
case class Label[A](name: String)
case class LabelWithValue[A](label: Label[A], value: A)
// TODO: avoid the horrible global state - pass in the Map as a parameter
var horribleGlobalState: Map[String, Any] = _
object GetLabelWithValue extends (Label ~> LabelWithValue) {
def apply[A](label: Label[A]) =
LabelWithValue(label, horribleGlobalState.get(label.name).asInstanceOf[A])
}
val label1 = Label[Int]("a")
val label2 = Label[String]("b")
val labels = label1 :: label2 :: HNil
val labelsWithValues: LabelWithValue[Int] :: LabelWithValue[String] :: HNil = getValues(labels)
println(labelsWithValues)
def getValues[L <: HList, M <: HList](labels: L)(
implicit mapper: Mapper.Aux[GetLabelWithValue.type, L, M]) = {
horribleGlobalState = Map("a" -> 5, "b" -> "five")
labels map GetLabelWithValue
}
}
Here is an alternative implementation of GetLabelWithValue, which behaves the same way:
object GetLabelWithValue extends Poly1 {
implicit def caseLabel[A] = at[Label[A]] { label ⇒
LabelWithValue(label, horribleGlobalState.get(label.name).asInstanceOf[A])
}
}
I am by no means shapeless guru but here's first thing that comes to my mind:
object Main extends App {
case class Label[A](name: String)
case class LabelWithValue[A](label: Label[A], value: A)
object combine extends Poly2 {
implicit def workS[A <: HList, B] = at[Label[B], (Map[String, Any], A)] {
case (i, (map, res)) ⇒
(map, LabelWithValue(i, map.get(i.name).asInstanceOf[B]) :: res)
}
}
var state: Map[String, Any] = Map("a" -> 5, "b" -> "five")
val label1 = Label[Int]("a")
val label2 = Label[String]("b")
val labels = label1 :: label2 :: HNil
val mapped = labels.foldRight((state, HNil))(combine)._2
println(mapped)
}
I'm not saying there's not better way, but this seems pretty reasonable - instead of global state you capture it using fold and decide based on it. Probably gives you a bit more power than you need though (as you could mutate the map inbetween folds, but...)
Here's the full solution (based on KadekM's solution) when you want to use it in a method. The hard bit was to extract the type out of the tuple (which is the result of the fold).
import shapeless._
import shapeless.ops.hlist._
import shapeless.ops.tuple.IsComposite
object Main extends App {
case class Label[A](name: String)
case class LabelWithValue[A](label: Label[A], value: A)
object combineLabelWithValue extends Poly2 {
implicit def atLabel[A, B <: HList] = at[Label[A], (B, Map[String, Any])] {
case (label, (acc, values)) ⇒
(LabelWithValue(label, values.get(label.name).asInstanceOf[A]) :: acc, values)
}
}
val label1 = Label[Int]("a")
val label2 = Label[String]("b")
val labels = label1 :: label2 :: HNil
val labelsWithValues: LabelWithValue[Int] :: LabelWithValue[String] :: HNil = getValues(labels)
println(labelsWithValues)
def getValues[L <: HList, Out, P](labels: L)(
implicit folder: RightFolder.Aux[L, (HNil.type, Map[String, Any]), combineLabelWithValue.type, P],
ic: IsComposite.Aux[P, Out, _]
): Out = {
val state = Map("a" -> 5, "b" -> "five")
val resultTuple = labels.foldRight((HNil, state))(combineLabelWithValue)
ic.head(resultTuple)
}
}
I am using a RightFolder that returns a Tuple2 and would like to return the _1 part. The first version rightFoldUntupled1 works fine but uses an additional IsComposite typeclass.
In the second version rightFoldUntupled2 I try to achieve the same without IsComposite by destructuring P as a Product2[_, Values]. That doesn't work any more - the compiler is happy to witness that P is a Product2[_,_], but as soon as I use a named type parameter for _1 it doesn't compile any more. Any idea why?
The full source (with sbt ready to ~run with implicit debug output) is here: https://github.com/mpollmeier/shapeless-playground/tree/f3cf049
case class Label[A](name: String)
val label1 = Label[Int]("a")
val labels = label1 :: HNil
object getValue extends Poly2 {
implicit def atLabel[A, B <: HList] = at[Label[A], (B, Map[String, Any])] {
case (label, (acc, values)) ⇒
(values(label.name).asInstanceOf[A] :: acc, values)
}
}
// compiles fine
val untupled1: Int :: HNil = rightFoldUntupled1(labels)
// [error] could not find implicit value for parameter folder:
// shapeless.ops.hlist.RightFolder.Aux[shapeless.::[Main.DestructureTupleTest.Label[Int],shapeless.HNil],
//(shapeless.HNil, Map[String,Any]),Main.DestructureTupleTest.getValue.type,P]
val untupled2: Int :: HNil = rightFoldUntupled2(labels)
def rightFoldUntupled1[L <: HList, P <: Product2[_, _], Values <: HList](labels: L)(
implicit folder: RightFolder.Aux[L, (HNil, Map[String, Any]), getValue.type, P],
ic: IsComposite.Aux[P, Values, _]
): Values = {
val state = Map("a" -> 5, "b" -> "five")
val resultTuple = labels.foldRight((HNil: HNil, state))(getValue)
ic.head(resultTuple)
}
def rightFoldUntupled2[L <: HList, Values, P <: Product2[_, Values]](labels: L)(
implicit folder: RightFolder.Aux[L, (HNil, Map[String, Any]), getValue.type, P]
): Values = {
val state = Map("a" -> 5, "b" -> "five")
val resultTuple = labels.foldRight((HNil: HNil, state))(getValue)
resultTuple._1
}
This should work same as rightFoldUntupled1:
def rightFoldUntupled2[L <: HList, Values, M](labels: L)(
implicit folder: RightFolder.Aux[L, (HNil, Map[String, Any]), getValue.type, (Values, M)]
): Values = {
val state = Map("a" -> 5, "b" -> "five")
val resultTuple = labels.foldRight((HNil: HNil, state))(getValue)
resultTuple._1
}
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
I have this helper function:
def findByType[T: ClassTag](xs: Seq[Any]) =
xs.find(classTag[T].runtimeClass.isInstance).map(_.asInstanceOf[T])
which I'm currently using like this:
val foo = findByType[Foo](xs)
val bar = findByType[Bar](xs)
val baz = findByType[Baz](xs)
However, there's some duplication here; what I'd like it something like (pseudocode):
val List(foo, bar, baz) = List(Foo, Bar, Baz).map(t => findByType[t](xs))
What are my options here? I can keep it the way it is, but if there's something simple to DRY this up, I've love to hear about it.
If you absolutely have to do this kind of thing, Shapeless can make it both cleaner and safer:
import shapeless._
def findByType[T](xs: Seq[Any])(implicit t: Typeable[T]) =
xs.flatMap(t.cast).headOption
Note that unlike your implementation, this one won't give wrong answers on many of the standard library's generic types:
scala> findByType[List[String]](Seq(List(1), List("a"), 'foo))
res3: Option[List[String]] = Some(List(a))
Vs.
scala> yourFindByType[List[String]](Seq(List(1), List("a"), 'foo))
res4: Option[List[String]] = Some(List(1))
(You still have to be careful, though, since this won't work on user-defined generics.)
It also lets you do neat stuff like this:
trait Sifter[L <: HList] extends DepFn1[Seq[Any]] {
type Out <: HList
}
object Sifter {
type Aux[L <: HList, Out0 <: HList] = Sifter[L] { type Out = Out0 }
implicit def emptySized: Aux[HNil, HNil] = new Sifter[HNil] {
type Out = HNil
def apply(xs: Seq[Any]) = HNil
}
implicit def otherSized[H, T <: HList, OutT <: HList](implicit
typeable: Typeable[H],
sifter: Aux[T, OutT]
): Aux[H :: T, Seq[H] :: OutT] = new Sifter[H :: T] {
type Out = Seq[H] :: OutT
def apply(xs: Seq[Any]) = xs.flatMap(typeable.cast) :: sifter(xs)
}
def sift[L <: HList](xs: Seq[Any])(implicit sifter: Sifter[L]): sifter.Out =
sifter(xs)
}
And then:
scala> val myStuff: Seq[Any] = List(1, List('a', 'b'), "foo", 'bar)
myStuff: Seq[Any] = List(1, List(a, b), foo, 'bar)
scala> val myInts :: myCharLists :: myStrings :: HNil =
| Sifter.sift[Int :: List[Char] :: String :: HNil](myStuff)
myInts: Seq[Int] = List(1)
myCharLists: Seq[List[Char]] = List(List(a, b))
myStrings: Seq[String] = List(foo)
Which is basically a type-safe version of what you want.
import scala.reflect.{ClassTag, classTag}
val List(foo, bar, baz) = List(classTag[Foo], classTag[Bar], classTag[Baz]).map(ct => findByType(xs)(ct))
Of course, now you're losing type information - foo, bar and baz will all have type Option[Any].