Example code:
trait A
case class B() extends A
case class C() extends A
trait D[T]
implicit object DB extends D[B]
implicit object DC extends D[C]
def getImplicit[T: D](arg: T) = implicitly[D[T]]
val list = Seq(B(), C())
list map getImplicit
how can i get implicits without explicitly casting objects in list? maybe i can do it with HList? or perhaps macros can help me?
i tried:
case class Wrap[T](v: T)
object getImplicit extends (Wrap ~> D) {
def apply[T](arg: Wrap[T]) = implicitly[D[T]]
}
val list = Wrap(B()) :: Wrap(C()) :: HNil
list map getImplicit
and i get compilation error:
could not find implicit value for parameter e: D[T]
def apply[T](arg: Wrap[T]) = implicitly[D[T]]
You will have to supply the type class to the apply method of getImplicit. Unfortunately that means you'll have to use the long form of Poly1:
object getImplicit extends Poly1 {
implicit def default[T: D] = at[Wrap[T]](_ => implicitly[D[T]])
}
After this change, your code will compile.
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'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.
My problem is that i want to mix in traits to some existing object instance
instead of doing
sealed trait Type
trait A extends Type
trait B extends Type
case class Basic(int, bool)
val someInt = 2
val someBool = False
val someType = "a"
someType match {
case "a" => new Basic(someInt, someBool) with A
case "b" => new Basic(someInt, someBool) with B
}
i would like to do new Basic() only once
and then add somehow A and B
Is it possible to do it somehow with Shapeless ?
I was hoping to be able to use Generic to get a HList, add the marker trait info in it, and convert it back to what would be a Basic with the trait attached. But I don't actually see how that would be possible with HList.
For example you can define a Poly.
import shapeless.Poly1
object myPoly extends Poly1 {
implicit val aCase: Case.Aux[A, Basic with A] = at(_ => new Basic() with A)
implicit val bCase: Case.Aux[B, Basic with B] = at(_ => new Basic() with B)
}
myPoly(new A {})
myPoly(new B {})
I guess HList is irrelevant here since Generic transforms Type into a coproduct rather than HList.
import shapeless.{:+:, CNil, Generic}
sealed trait Type
case class A() extends Type
case class B() extends Type
implicitly[Generic.Aux[Type, A :+: B :+: CNil]]
You can use Poly and singleton types:
import shapeless.{Poly1, Witness}
import shapeless.syntax.singleton._
sealed trait Type
trait A extends Type
trait B extends Type
case class Basic(int: Int, bool: Boolean)
val someInt: Int = 2
val someBool: Boolean = false
val someType: String = "a"
object myPoly extends Poly1 {
implicit val aCase: Case.Aux[Witness.`"a"`.T, Basic with A] = at(_ => new Basic(someInt, someBool) with A)
implicit val bCase: Case.Aux[Witness.`"b"`.T, Basic with B] = at(_ => new Basic(someInt, someBool) with B)
}
myPoly("a".narrow)
myPoly("b".narrow)
// myPoly("c".narrow) // doesn't compile
I want to define circular referenced structure JSON with spray-json, so I try to define like below.
final case class A(b: B)
final case class B(a: A)
trait AProtocol extends DefaultJsonProtocol {
implicit val aProtocol: RootJsonFormat[A] = rootFormat(lazyFormat(jsonFormat1(A)))
}
But I got an error which is
<console>:18: error: could not find implicit value for evidence parameter of type MyProtocol.this.JF[B]
implicit val aProtocol: RootJsonFormat[A] = rootFormat(lazyFormat(jsonFormat1(A)))
Please give me some advices.
Well, you have jsonFormat for A but what with B. You are using lazyFormat well but completely forgot about other dependency. Try this:
final case class A(b: B)
final case class B(a: A)
trait AProtocol extends DefaultJsonProtocol {
implicit val aProtocol: RootJsonFormat[A] = rootFormat(lazyFormat(jsonFormat1(A)))
implicit val bProtocol: RootJsonFormat[B] = rootFormat(lazyFormat(jsonFormat1(B)))
}
Trait GenericLinkedList , case class Cons and case object Nil were created like below.
The question is I want to use this genericLinkedList however as you know when we write this code var list = new GenericLinkedList , it will not cause Traits cannot create any object , Right? I want to create a class which extends GenericLinkedList but I cannot. How can I fix it ?
trait GenericLinkedList [+T] {
def prepend[TT >: T](x: TT): GenericLinkedList[TT] = this match {
case _ => Cons(x,this)
}
}
case class Cons[+T](head: T,tail: GenericLinkedList[T]) extends GenericLinkedList[T]
case object Nil extends GenericLinkedList[Nothing]
Your issue seems to be unable of doing
val list = new GenericLinkedList
Is your goal creating an empty list?
You can do
val list = new GenericLinkedList[Int] { }
since the trait is not abstract, but it's not pretty. You can alternatively define a companion object for your trait
object GenericLinkedList {
def apply[T](): GenericLinkedList[T] = Nil
}
and use it to initialize an empty list this way
scala> val x = GenericLinkedList[Int]()
// x: GenericLinkedList[Int] = Nil
scala> x.prepend(42)
// res0: GenericLinkedList[Int] = Cons(42,Nil)
By the way, the universal match in the prepend implementation is useless. You can just do
def prepend[TT >: T](x: TT): GenericLinkedList[TT] = Cons(x, this)