Conflicting method when paramter is limited to AnyRef and AnyVal in Scala - scala

Scala compiler detects the following two map functions as duplicates conflicting with each other:
class ADT {
def map[Output <: AnyVal](f: Int => Output): List[Output] = ???
def map[Output >: Null <: AnyRef](f: Int => Output): List[Output] = ???
}
The class type of Output parameter is different. First one limits to AnyVal and second one limits to AnyRef. How can I differentiate them?

The problem is not differentiating AnyVal from AnyRef so much as getting around the fact that both method signatures become the same after erasure.
Here is a neat trick to get around this kind of problem. It is similar to what #som-snytt did, but a bit more generic, as it works for other similar situations as well (e.g. def foo(f: Int => String): String = ??? ; def foo(f: String => Int): Int = ??? etc.):
class ADT {
def map[Output <: AnyVal](f: Int => Output): List[Output] = ???
def map[Output >: Null <: AnyRef](f: Int => Output)(implicit dummy: DummyImplicit): List[Output] = ???
}
The cutest thing is that this works "out of the box". Apparently, a DummyImplicit is a part of standard library, and you always have the thing in scope.
You can have more than two overloads this way too by just adding more dummies to the list.

scala 2.13.0-M5> :pa
// Entering paste mode (ctrl-D to finish)
object X {
def map[Output <: AnyVal](f: Int => Output) = 1
def map[O](f: Int => O)(implicit ev: O <:< AnyRef) = 2
}
// Exiting paste mode, now interpreting.
defined object X
scala 2.13.0-M5> X.map((x: Int) => x*2)
res0: Int = 1
scala 2.13.0-M5> X.map((x: Int) => "")
res1: Int = 2

You could use a typeclass for that map method.
Using your exact example:
trait MyTC[Output]{
def map(f: Int => Output): List[Output]
}
object MyTC{
def apply[A](a : A)(implicit ev : MyTC[A]) : MyTC[A] = ev
implicit def anyRefMyTc[A <: AnyRef] : MyTC[A] = new MyTC[A]{
def map(f: Int => A): List[A] = { println("inside sub-AnyRef"); List.empty }
}
implicit def anyValMyTc[A <: AnyVal] : MyTC[A] = new MyTC[A]{
def map(f: Int => A): List[A] = { println("inside sub-AnyVal"); List.empty }
}
}
import MyTC._
val r1 = Option("Test1")
val r2 = List(5)
val v1 = true
val v2 = 6L
// The functions here are just to prove the point, and don't do anything.
MyTC(r1).map(_ => None)
MyTC(r2).map(_ => List.empty)
MyTC(v1).map(_ => false)
MyTC(v2).map(_ => 10L)
That would print:
inside sub-AnyRef
inside sub-AnyRef
inside sub-AnyVal
inside sub-AnyVal
The advantage of this approach is that, should you then choose to specialise the behaviour further for just some specific type (e.g. say you want to do something specific for Option[String]), you can do that easily:
// This is added to MyTC object
implicit val optMyTc : MyTC[Option[String]] = new MyTC[Option[String]]{
def map(f: Int => Option[String]): List[Option[String]] = { println("inside Option[String]"); List.empty }
}
Then, re-running the code will print:
inside Option[String]
inside sub-AnyRef
inside sub-AnyVal
inside sub-AnyVal

Related

generalize Int -> Int, Int-> String, String -> String, String -> Int

I have 4 methods with one logic and 4 possible type mapping:
def convertStringToString(in: String): String = ???
def convertIntToString(in: Int): String = ???
def convertIntToInt(in: Int): Int = ???
def convertStringToInt(in: String): Int = ???
I want to generalize input and output type and write logic in one methods. Tried to generelize input parameter:
def convertToInt[IN](in: IN): Int = in match {
case x: String if x.forall(_.isDigit) => x.toInt
case y: Int => y
case _ => 0
}
def convertToString[IN](in: IN): String = convertToInt[IN](in).toString
Could you help me to generalize second:
def convertToInt[IN, OUT](in: IN): OUT = ???
If you really wanted to, you could have something typeclass-based:
def convert[I, O](in: I)(implicit c: ConversionRule[I, O]): O = {
if (c.isConvertible(in)) c.convert(in)
else c.zero
}
trait ConversionRule[I, O] {
def isConvertible(in: I): Boolean
def convert(in: I): O
def zero: O // Could possibly derive the zero from, e.g., a cats Monoid instance where such exists
}
The eagle-eyed may notice that the isConvertible/convert methods match the contract of PartialFunction[I, O]'s isDefinedAt/apply, so may as well just use PartialFunction (and rewrite convert with isDefinedAt/apply)
trait ConversionRule[I, O] extends PartialFunction[I, O] {
def zero: O
}
zero can be implemented in terms of PartialFunction.applyOrElse, but for the case where zero is constant (which is the case where referential transparency is preserved), this is much faster.
Smart constructors can be defined:
object ConversionRule {
def apply[I, O](zeroValue: O)(pf: PartialFunction[I, O]): ConversionRule[I, O] =
new ConversionRule[I, O] {
override def apply(i: I): O = pf(i)
override def isDefinedAt(i: I): Boolean = pf.isDefinedAt(i)
val zero: O = zeroValue
}
def totalConversion[I, O](f: I => O): ConversionRule[I, O] =
new ConversionRule[I, O] {
override def apply(i: I) = f(i)
override def isDefinedAt(i: I) = true
override def zero: O = throw new AssertionError("Should not call since conversion is defined")
}
// Might want to put this in a `LowPriorityImplicits` trait which this object extends
implicit def identityConversion[I]: ConversionRule[I, I] =
totalConversion(identity)
}
identityConversion means that a convertIntToInt gets automatically generated.
convertStringToInt can then be defined as
implicit val stringToIntConversion = ConversionRule[String, Int](0) {
case x if x.forAll(_.isDigit) => x.toInt
}
One can define a toString based conversion (basically the non-lawful Show proposed for alleycats):
implicit def genericToString[I]: ConversionRule[I, String] =
ConversionRule.totalConversionRule(_.toString)
And it should then be possible to define a stringViaInt ConversionRule derivation like:
implicit def stringViaInt[I, O](implicit toInt: ConversionRule[I, Int]): ConversionRule[I, String] =
convert(convert(in)(toInt))
The only really useful thing this provides is an opt-in to usage of implicit conversions. Whether that's enough of a gain to justify? shrug
(Disclaimer: only the scala compiler in my head has attempted to compile this)

Scala: reflection and case classes

The following code succeeds, but is there a better way of doing the same thing? Perhaps something specific to case classes? In the following code, for each field of type String in my simple case class, the code goes through my list of instances of that case class and finds the length of the longest string of that field.
case class CrmContractorRow(
id: Long,
bankCharges: String,
overTime: String,
name$id: Long,
mgmtFee: String,
contractDetails$id: Long,
email: String,
copyOfVisa: String)
object Go {
def main(args: Array[String]) {
val a = CrmContractorRow(1,"1","1",4444,"1",1,"1","1")
val b = CrmContractorRow(22,"22","22",22,"55555",22,"nine long","22")
val c = CrmContractorRow(333,"333","333",333,"333",333,"333","333")
val rows = List(a,b,c)
c.getClass.getDeclaredFields.filter(p => p.getType == classOf[String]).foreach{f =>
f.setAccessible(true)
println(f.getName + ": " + rows.map(row => f.get(row).asInstanceOf[String]).maxBy(_.length))
}
}
}
Result:
bankCharges: 3
overTime: 3
mgmtFee: 5
email: 9
copyOfVisa: 3
If you want to do this kind of thing with Shapeless, I'd strongly suggest defining a custom type class that handles the complicated part and allows you to keep that stuff separate from the rest of your logic.
In this case it sounds like the tricky part of what you're specifically trying to do is getting the mapping from field names to string lengths for all of the String members of a case class. Here's a type class that does that:
import shapeless._, shapeless.labelled.FieldType
trait StringFieldLengths[A] { def apply(a: A): Map[String, Int] }
object StringFieldLengths extends LowPriorityStringFieldLengths {
implicit val hnilInstance: StringFieldLengths[HNil] =
new StringFieldLengths[HNil] {
def apply(a: HNil): Map[String, Int] = Map.empty
}
implicit def caseClassInstance[A, R <: HList](implicit
gen: LabelledGeneric.Aux[A, R],
sfl: StringFieldLengths[R]
): StringFieldLengths[A] = new StringFieldLengths[A] {
def apply(a: A): Map[String, Int] = sfl(gen.to(a))
}
implicit def hconsStringInstance[K <: Symbol, T <: HList](implicit
sfl: StringFieldLengths[T],
key: Witness.Aux[K]
): StringFieldLengths[FieldType[K, String] :: T] =
new StringFieldLengths[FieldType[K, String] :: T] {
def apply(a: FieldType[K, String] :: T): Map[String, Int] =
sfl(a.tail).updated(key.value.name, a.head.length)
}
}
sealed class LowPriorityStringFieldLengths {
implicit def hconsInstance[K, V, T <: HList](implicit
sfl: StringFieldLengths[T]
): StringFieldLengths[FieldType[K, V] :: T] =
new StringFieldLengths[FieldType[K, V] :: T] {
def apply(a: FieldType[K, V] :: T): Map[String, Int] = sfl(a.tail)
}
}
This looks complex, but once you start working with Shapeless a bit you learn to write this kind of thing in your sleep.
Now you can write the logic of your operation in a relatively straightforward way:
def maxStringLengths[A: StringFieldLengths](as: List[A]): Map[String, Int] =
as.map(implicitly[StringFieldLengths[A]].apply).foldLeft(
Map.empty[String, Int]
) {
case (x, y) => x.foldLeft(y) {
case (acc, (k, v)) =>
acc.updated(k, acc.get(k).fold(v)(accV => math.max(accV, v)))
}
}
And then (given rows as defined in the question):
scala> maxStringLengths(rows).foreach(println)
(bankCharges,3)
(overTime,3)
(mgmtFee,5)
(email,9)
(copyOfVisa,3)
This will work for absolutely any case class.
If this is a one-off thing, you might as well use runtime reflection, or you could use the Poly1 approach in Giovanni Caporaletti's answer—it's less generic and it mixes up the different parts of the solution in a way I don't prefer, but it should work just fine. If this is something you're doing a lot of, though, I'd suggest the approach I've given here.
If you want to use shapeless to get the string fields of a case class and avoid reflection you can do something like this:
import shapeless._
import labelled._
trait lowerPriorityfilterStrings extends Poly2 {
implicit def default[A] = at[Vector[(String, String)], A] { case (acc, _) => acc }
}
object filterStrings extends lowerPriorityfilterStrings {
implicit def caseString[K <: Symbol](implicit w: Witness.Aux[K]) = at[Vector[(String, String)], FieldType[K, String]] {
case (acc, x) => acc :+ (w.value.name -> x)
}
}
val gen = LabelledGeneric[CrmContractorRow]
val a = CrmContractorRow(1,"1","1",4444,"1",1,"1","1")
val b = CrmContractorRow(22,"22","22",22,"55555",22,"nine long","22")
val c = CrmContractorRow(333,"333","333",333,"333",333,"333","333")
val rows = List(a,b,c)
val result = rows
// get for each element a Vector of (fieldName -> stringField) pairs for the string fields
.map(r => gen.to(r).foldLeft(Vector[(String, String)]())(filterStrings))
// get the maximum for each "column"
.reduceLeft((best, row) => best.zip(row).map {
case (kv1#(_, v1), (_, v2)) if v1.length > v2.length => kv1
case (_, kv2) => kv2
})
result foreach { case (k, v) => println(s"$k: $v") }
You probably want to use Scala reflection:
import scala.reflect.runtime.universe._
val rm = runtimeMirror(getClass.getClassLoader)
val instanceMirrors = rows map rm.reflect
typeOf[CrmContractorRow].members collect {
  case m: MethodSymbol if m.isCaseAccessor && m.returnType =:= typeOf[String] =>
    val maxValue = instanceMirrors map (_.reflectField(m).get.asInstanceOf[String]) maxBy (_.length)
    println(s"${m.name}: $maxValue")
}
So that you can avoid issues with cases like:
case class CrmContractorRow(id: Long, bankCharges: String, overTime: String, name$id: Long, mgmtFee: String, contractDetails$id: Long, email: String, copyOfVisa: String) {
val unwantedVal = "jdjd"
}
Cheers
I have refactored your code to something more reuseable:
import scala.reflect.ClassTag
case class CrmContractorRow(
id: Long,
bankCharges: String,
overTime: String,
name$id: Long,
mgmtFee: String,
contractDetails$id: Long,
email: String,
copyOfVisa: String)
object Go{
def main(args: Array[String]) {
val a = CrmContractorRow(1,"1","1",4444,"1",1,"1","1")
val b = CrmContractorRow(22,"22","22",22,"55555",22,"nine long","22")
val c = CrmContractorRow(333,"333","333",333,"333",333,"333","333")
val rows = List(a,b,c)
val initEmptyColumns = List.fill(a.productArity)(List())
def aggregateColumns[Tin:ClassTag,Tagg](rows: Iterable[Product], aggregate: Iterable[Tin] => Tagg) = {
val columnsWithMatchingType = (0 until rows.head.productArity).filter {
index => rows.head.productElement(index) match {case t: Tin => true; case _ => false}
}
def columnIterable(col: Int) = rows.map(_.productElement(col)).asInstanceOf[Iterable[Tin]]
columnsWithMatchingType.map(index => (index,aggregate(columnIterable(index))))
}
def extractCaseClassFieldNames[T: scala.reflect.ClassTag] = {
scala.reflect.classTag[T].runtimeClass.getDeclaredFields.filter(!_.isSynthetic).map(_.getName)
}
val agg = aggregateColumns[String,String] (rows,_.maxBy(_.length))
val fieldNames = extractCaseClassFieldNames[CrmContractorRow]
agg.map{case (index,value) => fieldNames(index) + ": "+ value}.foreach(println)
}
}
Using shapeless would get rid of the .asInstanceOf, but the essence would be the same. The main problem with the given code was that it was not re-usable since the aggregation logic was mixed with the reflection logic to get the field names.

Converting a List to a Case Class

As an exercise, I am trying to see if I can take a List[Any] and "cast" it into a case class using shapeless.
A very basic example of what I am trying to achieve:
case class Foo(i: Int, j: String)
val foo: Option[Foo] = fromListToCaseClass[Foo]( List(1:Any, "hi":Any) )
Here is how I am shaping my solution (this can be quite off):
def fromListToCaseClass[CC <: Product](a: List[Any]): Option[CC] = a.toHList[???].map( x => Generic[CC].from(x) )
Here is my reasoning:
I know that you can go from a case class to an HList[T] (CC -> HList[T]); where T is the type of the HList. I also know that you can create an HList from a list (list -> Option[HList]) as long as you know the type of the HList. Finally I know that you can go from an HList to a case class (HList -> CC).
CC -> HList[T]
list -> Option[HList[T]] -> Option[CC]
I am wondering if this makes sense or if I am way off here. Can we make this work? Any other suggestions? Thanks!
This can be done very straightforwardly using shapeless's Generic and FromTraversable type classes,
import scala.collection.GenTraversable
import shapeless._, ops.traversable.FromTraversable
class FromListToCaseClass[T] {
def apply[R <: HList](l: GenTraversable[_])
(implicit gen: Generic.Aux[T, R], tl: FromTraversable[R]): Option[T] =
tl(l).map(gen.from)
}
def fromListToCaseClass[T] = new FromListToCaseClass[T]
(There's some accidental complexity here due to Scala's awkwardness when it comes to mixing explicit and inferred type parameters: we want to specify T explicitly, but have R inferred for us).
Sample REPL session ...
scala> case class Foo(i: Int, j: String)
defined class Foo
scala> fromListToCaseClass[Foo](List(23, "foo"))
res0: Option[Foo] = Some(Foo(23,foo))
scala> fromListToCaseClass[Foo](List(23, false))
res1: Option[Foo] = None
You can do it with shapeless the following way:
import shapeless._
trait Creator[A] { def apply(list:List[Any]): Option[A] }
object Creator {
def as[A](list: List[Any])(implicit c: Creator[A]): Option[A] = c(list)
def instance[A](parse: List[Any] => Option[A]): Creator[A] = new Creator[A] {
def apply(list:List[Any]): Option[A] = parse(list)
}
def arbitraryCreate[A] = instance(list => list.headOption.map(_.asInstanceOf[A]))
implicit val stringCreate = arbitraryCreate[String]
implicit val intCreate = arbitraryCreate[Int]
implicit val hnilCreate = instance(s => if (s.isEmpty) Some(HNil) else None)
implicit def hconsCreate[H: Creator, T <: HList: Creator]: Creator[H :: T] =
instance {
case Nil => None
case list => for {
h <- as[H](list)
t <- as[T](list.tail)
} yield h :: t
}
implicit def caseClassCreate[C, R <: HList](
implicit gen: Generic.Aux[C, R],
rc: Creator[R]): Creator[C] =
instance(s => rc(s).map(gen.from))
}
And
val foo:Option[Foo] = Creator.as[Foo](List(1, "hi"))

Overcoming Scala Type Erasure For Function Argument of Higher-Order Function

Essentially, what I would like to do is write overloaded versions of "map" for a custom class such that each version of map differs only by the type of function passed to it.
This is what I would like to do:
object Test {
case class Foo(name: String, value: Int)
implicit class FooUtils(f: Foo) {
def string() = s"${f.name}: ${f.value}"
def map(func: Int => Int) = Foo(f.name, func(f.value))
def map(func: String => String) = Foo(func(f.name), f.value)
}
def main(args: Array[String])
{
def square(n: Int): Int = n * n
def rev(s: String): String = s.reverse
val f = Foo("Test", 3)
println(f.string)
val g = f.map(rev)
val h = g.map(square)
println(h.string)
}
}
Of course, because of type erasure, this won't work. Either version of map will work alone, and they can be named differently and everything works fine. However, it is very important that a user can call the correct map function simply based on the type of the function passed to it.
In my search for how to solve this problem, I cam across TypeTags. Here is the code I came up with that I believe is close to correct, but of course doesn't quite work:
import scala.reflect.runtime.universe._
object Test {
case class Foo(name: String, value: Int)
implicit class FooUtils(f: Foo) {
def string() = s"${f.name}: ${f.value}"
def map[A: TypeTag](func: A => A) =
typeOf[A] match {
case i if i =:= typeOf[Int => Int] => f.mapI(func)
case s if s =:= typeOf[String => String] => f.mapS(func)
}
def mapI(func: Int => Int) = Foo(f.name, func(f.value))
def mapS(func: String => String) = Foo(func(f.name), f.value)
}
def main(args: Array[String])
{
def square(n: Int): Int = n * n
def rev(s: String): String = s.reverse
val f = Foo("Test", 3)
println(f.string)
val g = f.map(rev)
val h = g.map(square)
println(h.string)
}
}
When I attempt to run this code I get the following errors:
[error] /src/main/scala/Test.scala:10: type mismatch;
[error] found : A => A
[error] required: Int => Int
[error] case i if i =:= typeOf[Int => Int] => f.mapI(func)
[error] ^
[error] /src/main/scala/Test.scala:11: type mismatch;
[error] found : A => A
[error] required: String => String
[error] case s if s =:= typeOf[String => String] => f.mapS(func)
It is true that func is of type A => A, so how can I tell the compiler that I'm matching on the correct type at runtime?
Thank you very much.
In your definition of map, type A means the argument and result of the function. The type of func is then A => A. Then you basically check that, for example typeOf[A] =:= typeOf[Int => Int]. That means func would be (Int => Int) => (Int => Int), which is wrong.
One of ways of fixing this using TypeTags looks like this:
def map[T, F : TypeTag](func: F)(implicit ev: F <:< (T => T)) = {
func match {
case func0: (Int => Int) #unchecked if typeOf[F] <:< typeOf[Int => Int] => f.mapI(func0)
case func0: (String => String) #unchecked if typeOf[F] <:< typeOf[String => String] => f.mapS(func0)
}
}
You'd have to call it with an underscore though: f.map(rev _). And it may throw match errors.
It may be possible to improve this code, but I'd advise to do something better. The simplest way to overcome type erasure on overloaded method arguments is to use DummyImplicit. Just add one or several implicit DummyImplicit arguments to some of the methods:
implicit class FooUtils(f: Foo) {
def string() = s"${f.name}: ${f.value}"
def map(func: Int => Int)(implicit dummy: DummyImplicit) = Foo(f.name, func(f.value))
def map(func: String => String) = Foo(func(f.name), f.value)
}
A more general way to overcome type erasure on method arguments is to use the magnet pattern. Here is a working example of it:
sealed trait MapperMagnet {
def map(foo: Foo): Foo
}
object MapperMagnet {
implicit def forValue(func: Int => Int): MapperMagnet = new MapperMagnet {
override def map(foo: Foo): Foo = Foo(foo.name, func(foo.value))
}
implicit def forName(func: String => String): MapperMagnet = new MapperMagnet {
override def map(foo: Foo): Foo = Foo(func(foo.name), foo.value)
}
}
implicit class FooUtils(f: Foo) {
def string = s"${f.name}: ${f.value}"
// Might be simply `def map(func: MapperMagnet) = func.map(f)`
// but then it would require those pesky underscores `f.map(rev _)`
def map[T](func: T => T)(implicit magnet: (T => T) => MapperMagnet): Foo =
magnet(func).map(f)
}
This works because when you call map, the implicit magnet is resolved at compile time using full type information, so no erasure happens and no runtime type checks are needed.
I think the magnet version is cleaner, and as a bonus it doesn't use any runtime reflective calls, you can call map without underscore in the argument: f.map(rev), and also it can't throw runtime match errors.
Update:
Now that I think of it, here magnet isn't really simpler than a full typeclass, but it may show the intention a bit better. It's a less known pattern than typeclass though. Anyway, here is the same example using the typeclass pattern for completeness:
sealed trait FooMapper[F] {
def map(foo: Foo, func: F): Foo
}
object FooMapper {
implicit object ValueMapper extends FooMapper[Int => Int] {
def map(foo: Foo, func: Int => Int) = Foo(foo.name, func(foo.value))
}
implicit object NameMapper extends FooMapper[String => String] {
def map(foo: Foo, func: String => String) = Foo(func(foo.name), foo.value)
}
}
implicit class FooUtils(f: Foo) {
def string = s"${f.name}: ${f.value}"
def map[T](func: T => T)(implicit mapper: FooMapper[T => T]): Foo =
mapper.map(f, func)
}

How to implement an heterogeneous container in Scala

I need an heterogeneous, typesafe container to store unrelated type A, B, C.
Here is a kind of type-level specification :
trait Container {
putA(a: A)
putB(b: B)
putC(c: C)
put(o: Any) = { o match {
case a: A => putA(a)
case b: B => putB(b)
case c: C => putC(c)
}
getAllAs : Seq[A]
getAllBs : Seq[B]
getAllCs : Seq[C]
}
Which type is best suites to backed this container ?
Is it worth creating a Containerable[T] typeclass for types A, B, C ?
thks.
As other have suggested, you can leverage shapeless' Coproduct type. Here's an example.
// let's define a Coproduct of the two types you want to support
type IS = Int :+: String :+: CNil
// now let's have a few instances
val i = Coproduct[IS](42)
val i2 = Coproduct[IS](43)
val s = Coproduct[IS]("foo")
val s2 = Coproduct[IS]("bar")
// let's put them in a container
val cont = List(i, s, i2, s2)
// now, do you want all the ints?
val ints = cont.map(_.select[Int]).flatten
// or all the strings?
val strings = cont.map(_.select[String]).flatten
// and of course you can add elements (it's a List)
val cont2 = Coproduct[IS](12) :: cont
val cont3 = Coproduct[IS]("baz") :: cont2
Now this is of course not the most intuitive API for a generic container, but can easily encapsulate the logic inside a custom class using a Coproduct for representing the multiple types.
Here's a sketch of an implementation
import shapeless._; import ops.coproduct._
class Container[T <: Coproduct] private (underlying: List[T]) {
def ::[A](a: A)(implicit ev: Inject[T, A]) =
new Container(Coproduct[T](a) :: underlying)
def get[A](implicit ev: Selector[T, A]) =
underlying.map(_.select[A]).flatten
override def toString = underlying.toString
}
object Container {
def empty[T <: Coproduct] = new Container(List[T]())
}
Example
scala> type IS = Int :+: String :+: CNil
defined type alias IS
scala> val cont = 42 :: "foo" :: "bar" :: 43 :: Container.empty[IS]
cont: Container[IS] = List(42, foo, bar, 43)
scala> cont.get[Int]
res0: List[Int] = List(42, 43)
scala> cont.get[String]
res1: List[String] = List(foo, bar)
Miles Sabin wrote a post on unboxed union types; this is implemented as a CoProduct in his shapeless library:
shapeless has a Coproduct type, a generalization of Scala's Either to an arbitrary number of choices
I am definitely not an expert on shapeless, but if you create a new question with or edit your question with the shapeless tag then you can get any assistance needed with using CoProduct
You should look at Shapeless's HList or Coproduct; I wouldn't reinvent this myself.
Here is a first version, but I would to abstract over type :
trait Container {
def putInt(i: Int)
def putString(s: String)
def put(o: Any) = o match {
case i: Int => putInt(i)
case s: String => putString(s)
}
def getInts() : Seq[Int]
def getStrings() : Seq[String]
}
class MutableContainer extends Container {
val ints = mutable.ArrayBuffer[Int]()
val strings = mutable.ArrayBuffer[String]()
override def putInt(i: Int): Unit = ints += i
override def putString(s: String): Unit = strings += s
override def getStrings(): Seq[String] = strings
override def getInts(): Seq[Int] = ints
}
object TestContainer extends App {
val mc = new MutableContainer()
mc.put("a")
mc.put("b")
mc.put(1)
println(mc.getInts())
println(mc.getStrings())
}
Now trying to abstract over type
trait Container {
def getInts() : Seq[Int]
def getStrings() : Seq[String]
def put[T](t: T)
//def get[T] : Seq[T]
}
class MutableContainer extends Container {
val entities = new mutable.HashMap[Class[_], mutable.Set[Any]]() with mutable.MultiMap[Class[_], Any]
override def getStrings(): Seq[String] = entities.get(classOf[String]).map(_.toSeq).getOrElse(Seq.empty).asInstanceOf[Seq[String]] //strings
override def getInts(): Seq[Int] = entities.get(classOf[Int]).map(_.toSeq).getOrElse(Seq.empty).asInstanceOf[Seq[Int]]
//override def get[T]: Seq[T] = entities.get(classOf[T]).map(_.toSeq).getOrElse(Seq.empty).asInstanceOf[Seq[T]]
override def put[T](t: T): Unit = entities.addBinding(t.getClass, t)
}
trait Containable[T] {
def typ : String
}
trait Cont {
implicit object IntContainable extends Containable[Int] {
override def typ: String = "Int"
}
implicit object StringContainable extends Containable[String] {
override def typ: String = "String"
}
}
object TestContainer extends App {
val mc = new MutableContainer()
mc.put("a")
mc.put("b")
mc.put(1)
println(mc.getInts())
println(mc.getStrings())
println(mc.entities.keys)
}
But i've got a problem with java.lang.Integer and Int…