I'm trying to convert a case class into another via conversion to HList.
case class Source(p1:S1, p2:S2) -> HList[S1:+:S2] -> HList[D1:+:D2] ->case class Destination(p1:D1,p2:D2)
I can convert from Source to HList via gem.to and from HList to Destination via gen.from.
I wrote a Converter for each type of parameter on Source to convert it to its corresponding type in Destination but I am unsure how to recursively traverse the HList. My attempt is shown below in hlistEncoder
trait Converter[T] {
def convert(t:T): Datastructure
}
object Converter {
implicit object StrDatastructure extends Converter[String]{
def convert(t:String) = Datastructure.Str(t)
}
implicit object NumDatastructure extends Converter[Double]{
def convert(t :Double) = Datastructure.Num(t)
}
implicit object IncDatastructure extends Converter[Inc]{
def convert(t :Inc) = Datastructure.Incc(t)
}
implicit def SeqDatastructure[T: Converter]: Converter[Seq[T]] = new Converter[Seq[T]]{
def convert(t: Seq[T]) = {
Datastructure.Listt(t.map(implicitly[Converter[T]].convert):_*)
}
}
//HList traversals
implicit object hnilDatastructure extends Converter[HNil]{
def convert(t: HNil) = Datastructure.Hnill(t)
}
implicit def hlistEncoder[H, T <: HList](implicit
hEncoder: Converter[H],
tEncoder: Converter[T]
): Converter[H :: T] = new Converter[H :: T] {
def apply(h:H, t:T)= {
case (h :: t) => hEncoder.convert(h) ++ tEncoder.convert(t)
}
}
}
I use this method to test HList to HList conversion
def convertToDatastructureN[T](x: T)(implicit converter: Converter[T]): Datastructure = {
converter.convert(x)
}
case class Inc(i:Int)
case class Source(i: Int, n:Inc)
val x = Generic[Source]
val xHlist = x.to(Source(99, Inc(5)))
convertToDatastructureN(xHlist)
Any ideas how to implement hlistEncoder?
I guess you have
sealed trait Datastructure
object Datastructure {
case class Str(t: String) extends Datastructure
case class Num(t: Double) extends Datastructure
case class Incc(t: Inc) extends Datastructure
case class Listt(t: Datastructure*) extends Datastructure
case class Hnill(t: HNil) extends Datastructure
}
You want your type class Converter to transform T into Datastructure. But also (HList[S1:+:S2] -> HList[D1:+:D2], where I guess :: should be instead of :+:) you want subtypes of HList to be transformed into subtypes of HList (not into HList itself since otherwise Generic can't restore case class). So either you should modify your type class
trait Converter[T] {
type Out
def convert(t:T): Out
}
or you need two type classes: your original Converter and
trait HListConverter[T <: HList] {
type Out <: HList
def convert(t:T): Out
}
Moreover, currently your Converter is pretty rough. It transforms every T into Datastructure instead of into specific subtypes of Datastructure. This means Generic will be able to restore case classes only of shapes
MyClass(x: Datastructure)
MyClass(x: Datastructure, y: Datastructure)
...
Is it really what you want? If so then ok, if not and you need
MyClass(x: Str)
MyClass(x: Num, y: Incc)
...
then again you need
trait Converter[T] {
type Out
def convert(t:T): Out
}
Instead of HListConverter you can use standard shapeless.ops.hlist.Mapper.
Related
I want to leverage Scala reflection to implicitly pass a ClassTag.
There are plenty solutions on StackOverflow on how to accomplish this.
import scala.reflect.ClassTag
// animal input
trait AnimalInput {}
case class DogInput() extends AnimalInput
case class CatInput() extends AnimalInput
// animals
trait Animal[T <: AnimalInput] {
def getInput(implicit ct: ClassTag[T]): Class[T] = {
ct.runtimeClass.asInstanceOf[Class[T]]
}
}
object Dog extends Animal[DogInput] {}
object Cat extends Animal[CatInput] {}
I can test that this is working well:
println(Dog.getInput) // Success: "DogInput"
println(Cat.getInput) // Success: "CatInput"
The problem is, the second I reference these objects in any collection, I run into trouble:
// Failure: "No ClassTag available for animal.T"
List(Dog, Cat).foreach(animal => println(animal.getInput))
I think I understand why this is happening but I'm not sure how to work around it.
Thank you in advance for your help!
Actually, I can't reproduce No ClassTag available for animal.T. Your code compiles and runs in Scala 2.13.9:
https://scastie.scala-lang.org/DmytroMitin/lonnNg0fR1qb4Lg7ChDBqg
Maybe by failure you meant that for collection you don't receive classes DogInput, CatInput.
Actually, I managed to reproduce No ClassTag available for animal.T for type member T <: AnimalInput rather than type parameter: https://scastie.scala-lang.org/v79Y37eDRXuoauWA9b8uhQ
This question is very close to recent question Trying to extract the TypeTag of a Sequence of classes that extend a trait with different generic type parameters
See the reasons there.
Either use a heterogeneous collection
object classPoly extends Poly1 {
implicit def cse[T <: AnimalInput : ClassTag, A](implicit
ev: A <:< Animal[T]
): Case.Aux[A, Class[T]] =
at(_ => classTag[T].runtimeClass.asInstanceOf[Class[T]])
}
(Dog :: Cat :: HNil).map(classPoly).toList.foreach(println)
// class DogInput
// class CatInput
or use runtime reflection
trait Animal[T <: AnimalInput] {
def getInput: Class[T] = {
val classSymbol = runtimeMirror.classSymbol(this.getClass)
val animalSymbol = typeOf[Animal[_]].typeSymbol
val extendeeType = classSymbol.typeSignature.baseType(animalSymbol)
val extenderSymbol = extendeeType.typeArgs.head.typeSymbol.asClass
runtimeMirror.runtimeClass(extenderSymbol).asInstanceOf[Class[T]]
}
}
List(Dog, Cat).foreach(animal => println(animal.getInput))
// class DogInput
// class CatInput
The easiest is
trait Animal[T <: AnimalInput] {
def getInput: Class[T]
}
object Dog extends Animal[DogInput] {
val getInput = classOf[DogInput]
}
object Cat extends Animal[CatInput] {
val getInput = classOf[CatInput]
}
One more option is magnet pattern (1 2 3 4 5 6)
trait AnimalMagnet[T <: AnimalInput] {
def getInput: Class[T]
}
import scala.language.implicitConversions
implicit def animalToMagnet[A, T <: AnimalInput : ClassTag](a: A)(implicit
ev: A <:< Animal[T]
): AnimalMagnet[T] = new AnimalMagnet[T] {
override def getInput: Class[T] = classTag[T].runtimeClass.asInstanceOf[Class[T]]
}
List[AnimalMagnet[_]](Dog, Cat).foreach(animal => println(animal.getInput))
//class DogInput
//class CatInput
Also you can move ClassTag implicit from the method to the trait (and make the trait an abstract class)
abstract class Animal[T <: AnimalInput](implicit ct: ClassTag[T]) {
def getInput: Class[T] = ct.runtimeClass.asInstanceOf[Class[T]]
}
List(Dog, Cat).foreach(animal => println(animal.getInput))
// class DogInput
// class CatInput
I have the following type definitions:
trait Content
trait Wrapper {
type ContentType
}
final case class Foo(param: String) extends Content
final case class Bar(param: String) extends Content
final case class FooWrapper(foo: Foo) extends Wrapper { type ContentType = Foo }
final case class BarWrapper(bar: Bar) extends Wrapper { type ContentType = Bar }
Given a content value at runtime, I would like to return it wrapped in its corresponding wrapper type. I tried the following using Shapeless:
def fetchWrapper[N, G <: Wrapper](
implicit
gen: Generic.Aux[G, N :: HNil],
// this also compiles, as an alternative to Generics.Aux
// =:= :G#ValueType =:= N
) = ...
It works, but only if I explicitly provide the type parameters: fetchWrapper[Foo, FooWrapper]. How do I take advantage of implicit resolution to generalize things so that I can derived the correct wrapper for a given content?
I was thinking of generating an instance of the wrapper using the same derivation technique in the random number generator section of the shapeless book (i.e a typelcass that produces BarWrapper if I have an implicit Bar :: HNil in scope), but I can't even find the correct wrapper type in the first place.
Generic can easily help with transforming Wrapper subtype into Content subtype but you want vice versa.
Try a type class
trait ContentToWrapper[C <: Content] {
type Out <: Wrapper { type ContentType = C }
}
object ContentToWrapper {
implicit val foo: ContentToWrapper[Foo] { type Out = FooWrapper } = null
implicit val bar: ContentToWrapper[Bar] { type Out = BarWrapper } = null
}
def fetchWrapper[C <: Content](implicit ctw: ContentToWrapper[C]): ctw.Out = ???
If you make Wrapper sealed you can derive the type class
import shapeless.{Coproduct, Generic, HList, Poly1, poly}
import shapeless.ops.coproduct.ToHList
import shapeless.ops.hlist.CollectFirst
object ContentToWrapper {
implicit def mkContentToWrapper[C <: Content, WC <: Coproduct, WL <: HList](implicit
generic: Generic.Aux[Wrapper, WC],
toHList: ToHList.Aux[WC, WL], // there is CollectFirst for HList but not for Coproduct
collect: CollectFirst[WL, WrapperSubtypePoly[C]]
): ContentToWrapper[C] { type Out = collect.Out } = null
trait WrapperSubtypePoly[C] extends Poly1
object WrapperSubtypePoly {
implicit def cse[C, A <: Wrapper { type ContentType = C }]:
poly.Case1.Aux[WrapperSubtypePoly[C], A, A] = poly.Case1(identity)
}
}
Testing:
val w1 = fetchWrapper[Foo]
w1: FooWrapper
val w2 = fetchWrapper[Bar]
w2: BarWrapper
I need to provide type class instances for a bunch of case classes all derived from a single trait, but as far as I understand Scala compiler expects an instance for a specific class and doesn't go up the inheritance hierarchy. So this code:
trait Base
sealed trait Child extends Base
case class Concrete() extends Child
trait Printable[A] {
def print(value: A): String
}
object WtfTrait {
def print[A](x: A)(implicit ev: Printable[A]) = {
println(ev.print(x))
}
implicit val printableBase = new Printable[Base] {
override def print(value: Base): String = value.toString
}
val x = Concrete()
print(x)
}
doesn't compile with an error reading could not find implicit value for parameter ev: Printable[Impl]. Is there a way to define a single type class instance for the base trait and avoid repitition maybe by using Shapeless or something.
Guess you mean Printable[Concrete] (that's to say a Show typeclass instance).
Need to update to printableBase definition as bellow:
trait Base
sealed trait Child extends Base
case class Concrete() extends Child
trait Printable[A] {
def print(value: A): String
}
object WtfTrait {
def print[A](x: A)(implicit ev: Printable[A]) = {
println(ev.print(x))
}
// HERE
implicit def printableBase[T <: Base] = new Printable[T] {
override def print(value: T): String = value.toString
}
val x = Concrete()
print(x)
}
Printable can be made contravariant by adding a - sign:
trait Printable[-A]
This makes Printable[X] a subtype of Printable[Y] if Y is a subtype of X. In particular, Printable[Base] is a subtype of Printable[Concrete] and can be used when the compiler looks for an implicit of that type.
Given this case class case class Location(id: BigInt, lat: Double, lng: Double)
and a list of strings List("87222", "42.9912987", "-93.9557953")
I would like to do something like Location.fromString(listOfString) in a way Location instance is created with string types converted into appropriate types.
Only way I can think of is define fromString method in each case class but I'm looking for a more generic solution each class can inherit. For example, I would have case class Location(...) extends Record, where Record implements fromString that does conversion based on argument types defined in Location class.
Normally for best performance in such you need to create some macro.
Luckily there is beautiful shapeless library with which you could create such generic reader with near-macro performance in no time using it's generic case class representation
First define typeclass and some instances for reading your fields:
trait Read[T] {
def fromString(s: String): T
}
implicit object readBigInt extends Read[BigInt] {
def fromString(s: String): BigInt = BigInt(s)
}
implicit object readDouble extends Read[Double] {
def fromString(s: String): Double = s.toDouble
}
Next define your main typeclass:
trait ReadSeq[T] {
def fromStrings(ss: Seq[String]): T
}
Now it's shapeless time (thinking about DalĂ?) . We create reader for powerful Heterogenous List, which is almost like List but with of statically known length and element types.
It's no harder than to match simple List. Just define cases for empty list and cons:
import shapeless._
implicit object readHNil extends ReadSeq[HNil] {
def fromStrings(ss: Seq[String]) = HNil
}
implicit def readHList[X, XS <: HList](implicit head: Read[X], tail: ReadSeq[XS]) =
new ReadSeq[X :: XS] {
def fromStrings(ss: Seq[String]) = ss match {
case s +: rest => (head fromString s) :: (tail fromStrings rest)
}
}
Now we can use out-of-the-box macro mapping of HLists and case classes via Generic:
implicit def readCase[C, L <: HList](implicit gen: Generic.Aux[C, L], read: ReadSeq[L]) =
new ReadSeq[C] {
def fromStrings(ss: Seq[String]) = gen.from(read.fromStrings(ss))
}
Finally we could build our typeclass user:
def fromStringSeq[T](ss: Seq[String])(implicit read: ReadSeq[T]) = read.fromStrings(ss)
from this point having
case class Location(id: BigInt, lat: Double, lng: Double)
val repr = List("87222", "42.9912987", "-93.9557953")
you can ensure that
fromStringSeq[Location](repr) == Location(BigInt("87222"), 42.9912987, 93.9557953)
I often find that I need to extract the type of a sealed trait before doing the same thing to each implementation:
sealed trait Trait
case class Foo() extends Trait
case class Bar() extends Trait
// ... lots of other implementations
// *must* take a `Trait`, not a `T <: Trait`
def thing(t: Trait): ??? = t match {
case f: Foo => // something with the instance and specific type
case b: Bar => // something with the instance and specific type
// ... same thing again for other implementations
}
for example
// typically provided by somebody else...
trait Thing[T] { def thingy: String }
implicit def thing[T]: Thing[T] = new Thing[T] { def thingy = "stuff" }
def thing(t: Trait): String = t match {
case Foo() => implicitly[Thing[Foo]].thingy
case Bar() => implicitly[Thing[Bar]].thingy
// ...
}
I'd like to reduce the boilerplate involved in doing this.
UPDATE: nowadays we'd use typeclass derivation via shapeless. e.g. https://github.com/fommil/shapeless-for-mortals
It turns out that you can use shapeless' polymorphic functions and co-product to do this:
object thing extends Poly1 {
implicit def action[T <: Trait: Thing] = at[T](
a => implicitly[Thing[T]].thingy
)
// magic that makes it work at the sealed trait level
def apply(t: Trait): String =
Generic[Trait].to(t).map(thing).unify
}
which can then be used like
println(thing(Foo(): Trait))
I'd like to make this easier to write via an abstract class (let's forget about passing on implicit parameters to action for now), e.g.
abstract class MatchSealed[In, Out] extends Poly1 {
implicit def poly[T <: In] = at[T](action)
def action[T <: In](t: T): Out
import ops.coproduct.Mapper
def apply[R <: HList](in: In)(
implicit
g: Generic.Aux[In, R],
m: Mapper[this.type, R]
): Out = {
val self: this.type = this
g.to(in).map(self).unify
}
}
but this is failing with a missing Mapper[self.type, g.Repr] on the final line. I don't know which implicit is missing, but I suspect it is the self.type. I really want to capture realisedSelf.type but I don't know how to do that.
UPDATE: it turns out that it is not possible to obtain the Mapper because it needs access to the realised object Unable to map on HList