How to implement an heterogeneous container in Scala - 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…

Related

Conflicting method when paramter is limited to AnyRef and AnyVal in 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

Type safe type combinations with shapeless

Having the following code:
import shapeless.{::, Generic, HList, HNil, Lazy}
object Problem {
trait In {
def bar: Double
}
trait A {
def foo: Int
type I <: In
}
/////////////////////////////////////////////////////
final case class A1In(d: Double) extends In {
override def bar: Double = 1.1 + d
}
final case class A1() extends A {
override def foo: Int = 1
override type I = A1In
}
final case class A2In(d: Double) extends In {
override def bar: Double = 1.1 + d
}
final case class A2() extends A {
override def foo: Int = 1
override type I = A2In
}
final case class AListIn[T <: HList](items: T)(implicit ev: isIn[T]) extends In {
override def bar = 1.1
}
final case class AList[T <: HList](items: T)(implicit ev: isA[T]) extends A {
override def foo: Int = 555
override type I = AListIn[???]
}
trait isA[T] {
def aux_foo(value: T): Int
}
trait isIn[T] {
def aux_bar(value: T): Double
}
/////////////////////////////////////////////////////
def alloc(a: A): In = ????
def usage() = {
val a1: A1 = A1()
val a2: A2 = A2()
val l: AList[::[A1, ::[A2, ::[A1, HNil]]]] = AList(a1 :: a2 :: a1 :: HNil)
val a1In: A1In = A1In(1.2)
val a2In: A2In = A2In(9.3)
val lIn: AListIn[::[A2In, ::[A1In, HNil]]] = AListIn(a2In :: a1In :: HNil)
}
}
How can I fix it so it works as expected?
E.g how do I get correct type in place of ??? which is a proper type HList being result of applying isA -> isIn type mapping. The mapping must follow the natural association of A -> In mapping defined as type I <: In in trait A
And how to implement alloc function which for any concrete instance of In will produce corresponding instance of A?
Should concrete implementations of In be path dependent types of corresponding As?
Below is the code for isA the code for isIn is analogous
trait isA[T] {
def aux_foo(value: T): Int
}
object isA {
// "Summoner" method
def apply[T](implicit enc: isA[T]): isA[T] = enc
// "Constructor" method
def instance[T](func: T => Int): isA[T] = new isA[T] {
override def aux_foo(value: T): Int = func(value)
}
implicit def a1Encoder: isA[A1] = instance(i => 4)
implicit def a2Encoder: isA[A2] = instance(i => 9)
implicit def hnilEncoder: isA[HNil] = instance(hnil => 0)
implicit def hlistEncoder[H, T <: HList](implicit
hInstance: Lazy[isA[H]],
tInstance: isA[T]
): isA[H :: T] = instance {
case h :: t => hInstance.value.aux_foo(h) + tInstance.aux_foo(t)
}
implicit def genericInstance[A, R](implicit
generic: Generic.Aux[A, R],
rInstance: Lazy[isA[R]]
): isA[A] = instance { value => rInstance.value.aux_foo(generic.to(value)) }
}

Lifting a function which takes implicit parameter using functor (Scalaz7)

Just started learning Scalaz. Here is my code
trait Monoid[A] {
def mappend(a1: A, a2: A): A
def mzero: A
}
object Monoid {
implicit val IntMonoid: Monoid[Int] = new Monoid[Int] {
def mappend(a1: Int, a2: Int): Int = a1 + a2
def mzero: Int = 0
}
implicit val StringMonoid: Monoid[String] = new Monoid[String] {
def mappend(a1: String, a2: String): String = a1 + a2
def mzero: String = ""
}
}
trait MonoidOp[A] {
val F: Monoid[A]
val value: A
def |+|(a2: A): A = F.mappend(value, a2)
}
object MonoidOp{
implicit def toMonoidOp[A: Monoid](a: A): MonoidOp[A] = new MonoidOp[A]{
val F = implicitly[Monoid[A]]
val value = a
}
}
I have defined a function (just for the sake of it)
def addXY[A: Monoid](x: A, y: A): A = x |+| y
I want to lift it so that it could be used using Containers like Option, List, etc. But when I do this
def addXYOptioned = Functor[Option].lift(addXY)
It says error: could not find implicit value for evidence parameter of type scalaz.Monoid[A]
def addOptioned = Functor[Option].lift(addXY)
How to lift such functions?
Your method addXY needs a Monoid[A] but there is no Monoid[A] in scope when used in addXYOptioned, so you also need to add the Monoid constraint to addXYOptioned.
The next problem is that Functor.lift only lifts a function A => B, but we can use Apply.lift2 to lift a function (A, B) => C.
Using the Monoid from Scalaz itself :
import scalaz._, Scalaz._
def addXY[A: Monoid](x: A, y: A): A = x |+| y
def addXYOptioned[A: Monoid] = Apply[Option].lift2(addXY[A] _)
We could generalize addXYOptioned to make it possible to lift addXY into any type constructor with an Apply instance :
def addXYApply[F[_]: Apply, A: Monoid] = Apply[F].lift2(addXY[A] _)
addXYApply[List, Int].apply(List(1,2), List(3,4))
// List[Int] = List(4, 5, 5, 6)
addXYApply[Option, Int].apply(1.some, 2.some)
// Option[Int] = Some(3)

Pulling out shapeless polymorphic functions that have dependencies

New to shapeless and I have a question on using polymorphic functions that need some dependencies. I basically have this code and want to pull somePoly object out of the run method:
import shapeless._
object SomeObject {
type SomeType = Int :+: String :+: (String, Int) :+: CNil
def run( someList: List[SomeType], someInt:Int, someWord:String ) = {
object somePoly extends Poly1 {
implicit def doIt = at[Int]( i => i + someInt + someWord.length)
implicit def doIt2 = at[String]( i => i.length + someWord.length)
implicit def doIt3 = at[(String, Int)]( i => i._1.length + someWord.length)
}
someList.map( _.map(somePoly) )
}
}
One way I thought of doing it was like this, but it seems messy:
object TypeContainer {
type SomeType = Int :+: String :+: (String, Int) :+: CNil
}
case class SomePolyWrapper( someList: List[TypeContainer.SomeType], someInt:Int, someWord:String ){
object somePoly extends Poly1 {
implicit def doIt = at[Int]( i => i + someInt + someWord.length)
implicit def doIt2 = at[String]( i => i.length + someWord.length)
implicit def doIt3 = at[(String, Int)]( i => i._1.length + someWord.length)
}
}
object SomeObject {
def run( someList: List[TypeContainer.SomeType], someInt:Int, someWord:String ) = {
val somePolyWrapper = SomePolyWrapper(someList, someInt, someWord)
someList.map( _.map(somePolyWrapper.somePoly) )
}
}
Anyone have any advice?
The limitations of Scala's implicit resolution system mean the Poly definition needs to be a stable identifier, which makes this kind of thing more painful than it should be. As I mentioned on Gitter, there are a couple of workarounds that I know of (there may be others).
One approach would be to make the Poly1 a PolyN, where the extra arguments are for the someInt and someWord values. If you were mapping over an HList, you'd then use mapConst and zip to make the input HList have the right shape. I've never done this for a coproduct, but something similar is likely to work.
Another approach is to use a custom type class. In your case that might look something like this:
import shapeless._
trait IntFolder[C <: Coproduct] {
def apply(i: Int, w: String)(c: C): Int
}
object IntFolder {
implicit val cnilIntFolder: IntFolder[CNil] = new IntFolder[CNil] {
def apply(i: Int, w: String)(c: CNil): Int = sys.error("Impossible")
}
def instance[H, T <: Coproduct](f: (H, Int, String) => Int)(implicit
tif: IntFolder[T]
): IntFolder[H :+: T] = new IntFolder[H :+: T] {
def apply(i: Int, w: String)(c: H :+: T): Int = c match {
case Inl(h) => f(h, i, w)
case Inr(t) => tif(i, w)(t)
}
}
implicit def iif[T <: Coproduct: IntFolder]: IntFolder[Int :+: T] =
instance((h, i, w) => h + i + w.length)
implicit def sif[T <: Coproduct: IntFolder]: IntFolder[String :+: T] =
instance((h, i, w) => h.length + i + w.length)
implicit def pif[T <: Coproduct: IntFolder]: IntFolder[(String, Int) :+: T] =
instance((h, i, w) => h._1.length + i + w.length)
}
And then you could write a more generic version of your run:
def run[C <: Coproduct](
someList: List[C],
someInt: Int,
someWord: String
)(implicit cif: IntFolder[C]): List[Int] = someList.map(cif(someInt, someWord))
And use it like this:
scala> run(List(Coproduct[SomeType](1)), 10, "foo")
res0: List[Int] = List(14)
scala> run(List(Coproduct[SomeType](("bar", 1))), 10, "foo")
res1: List[Int] = List(16)
The specificity of the operation makes this approach look a little weird, but if I really needed to do something like this for different coproducts, this is probably the solution I'd choose.

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"))