Generic method which works with RDD and Seq - scala

I would like to write a method which would accept RDD and Seq without having to duplicate my code.
def myMethod[F[_]](input: F[InputClass]): F[OutputClass] = {
// do something here like
input.map{ i =>
// transformed input OutputClass
}
}
F could be Seq or RDD since they both have the method map implemented.
For more unique method like count or cache can I make the Seq do nothing for cache and use length for count?

What you want is a Type Class.
If you only need map and flatMap methods, I would recommend you to use a Monad (maybe the Cats one) and provide a the implementation for RDD.
Now, if you want more methods, you can implement your own Type Class.
import scala.language.higherKinds
trait DataCollection[F[_]] {
def map[A, B](col: F[A])(f: A => B): F[B]
def cache[A](col: F[A]): F[A]
def count[A](col: F[A]): Long
}
object DataCollection {
implicit val RddDataCollection: DataCollection[RDD] = new DataCollection[RDD] {
override def map[A, B](rdd: RDD[A])(f: A => B): RDD[B] = rdd.map(f)
override def cache[A](rdd: RDD[A]): RDD[A] = rdd.cache()
override def count[A](rdd: RDD[A]): Long = rdd.count()
}
implicit val SeqDataCollection: DataCollection[Seq] = new DataCollection[Seq] {
override def map[A, B](seq: Seq[A])(f: A => B): Seq[B] = seq.map(f)
override def cache[A](seq: Seq[A]): Seq[A] = seq
override def count[A](seq: Seq[A]): Long = seq.length
}
implicit class Ops[F[_], A](val col: F[A]) extends AnyVal {
#inline
def map[B](f: A => B)(implicit DC: DataCollection[F]): F[B] = DC.map(col)(f)
#inline
def cache()(implicit DC: DataCollection[F]): F[A] = DC.cache(col)
#inline
def count()(implicit DC: DataCollection[F]): Long = DC.count(col)
}
}
def myGenericMethod[F[_]: DataCollection, T](col: F[T]): Long = {
import DataCollection.Ops
col.map(x => x).cache().count()
}

Related

Get case class parameter types as a HList

I'm trying to generate instances of case class using shapeless
This works for generating instances of Foo
case class Foo(x: Int, y: String)
class Context {
val random = new Random()
}
def genInt(context: Context): Int = {
context.random.nextInt()
}
def genString(context: Context): String = {
context.random.nextString(16)
}
object ClassesToGenerators extends Poly1 {
implicit def caseInt = at[Class[Int]](_ => genInt(_))
implicit def caseString = at[Class[String]](_ => genString(_))
}
val gen = Generic[Foo]
val context = new Context()
val classes = classOf[Int] :: classOf[String] :: HNil // can't figure out how to get this hlist programmatically
val generators = classes.map(ClassesToGenerators)
gen.from(generators.zipApply(generators.mapConst(context)))
However, I'm aiming to write something reusable like
def newInstance[T] -> T:
???
which could generate instances of any case classes that takes only int and string parameters.
As mentioned in the code snippet, I'm stuck at getting a hlist of case class attribute types i.e. would like to convert case class Foo(x: Int, y: String) to classOf[Int] :: classOf[String] :: HNil. Any other approaches to this problem are also very appreciated but I'm not looking for a generic way of generating random instances of cases classes (as my use-case is different and used random generator just as an example)
IMHO, Shapeless is better used when you forget about all the fancy stuff and just focus on simple typeclass derivation using HList like this:
import shapeless.{Generic, HList, HNil, :: => :!:}
import scala.util.Random
trait Context {
def random: Random
}
object Context {
object implicits {
implicit final val global: Context = new Context {
override final val random: Random = new Random()
}
}
}
trait Generator[A] {
def generate(context: Context): A
}
object Generator {
final def apply[A](implicit ev: Generator[A]): ev.type = ev
final def generate[A](implicit ev: Generator[A], ctx: Context): A =
ev.generate(ctx)
implicit final val IntGenerator: Generator[Int] =
new Generator[Int] {
override def generate(context: Context): Int =
context.random.nextInt()
}
implicit final val StringGenerator: Generator[String] =
new Generator[String] {
override def generate(context: Context): String =
context.random.nextString(16)
}
implicit final def auto[P <: Product](implicit ev: GeneratorGen[P]): Generator[P] = ev
}
sealed trait GeneratorRepr[R <: HList] extends Generator[R]
object GeneratorRepr {
implicit final val HNilGeneratorRepr: GeneratorRepr[HNil] =
new GeneratorRepr[HNil] {
override def generate(context: Context): HNil =
HNil
}
implicit final def HConsGeneratorRepr[E, T <: HList](
implicit ev: Generator[E], tail: GeneratorRepr[T]
): GeneratorRepr[E :!: T] =
new GeneratorRepr[E :!: T] {
override def generate(context: Context): E :!: T =
ev.generate(context) :: tail.generate(context)
}
}
sealed trait GeneratorGen[P <: Product] extends Generator[P]
object GeneratorGen {
implicit final def instance[P <: Product, R <: HList](
implicit gen: Generic.Aux[P, R], ev: GeneratorRepr[R]
): GeneratorGen[P] = new GeneratorGen[P] {
override def generate(context: Context): P =
gen.from(ev.generate(context))
}
}
Which can be used like this:
import Context.implicits.global
final case class Foo(x: Int, y: String)
val result = Generator.generate[Foo]
// result: Foo = Foo(-2127375055, "鞰Ϗƨ⹼沺㗝䚮Ⴍ욏ꖱꬮӝ闉믃雦峷")
You can see the code running here.
Using built-in Shapeless type classes you can do
import shapeless.ops.hlist.FillWith
import shapeless.{Generic, HList, Poly0}
val context = new Context()
object ClassesToGenerators extends Poly0 {
implicit val caseInt = at[Int](genInt(context))
implicit val caseString = at[String](genString(context))
}
def newInstance[A] = new PartiallyApplied[A]
class PartiallyApplied[A] {
def apply[L <: HList]()(implicit
generic: Generic.Aux[A, L],
fillWith: FillWith[ClassesToGenerators.type, L]
): A = generic.from(fillWith())
}
newInstance[Foo]() // Foo(2018031886,⮐掐禃惌ᰧ佨妞꨸ዤࠒ훿柲籐妭蝱⻤)

How to use implicit conversion instead of structural typing when only one collection method is needed

I have a method in which I only need to access one method (appended, or :+) of a type. I want to be able to use both a custom case class and a normal List. Coming from a TypeScript background, I know how to do this using structural typing:
case class CustomContainer[T](data: List[T]) {
def :+(other: T): CustomContainer[T] = copy(data = data :+ other)
}
def append[T, A <: { def :+(other: T): A }](appendable: A, other: T): A = appendable :+ other
append(List(1,2,3), 4) // List(1,2,3,4)
append(CustomContainer(List(1,2,3)), 4) // CustomContainer(List(1,2,3,4))
However, I also know there are performance and maintainability disadvantages to using structural types. I'd like to find a better way, so I'm trying to do this using traits instead, but the sticking point is that I can't make List extend some trait that I defined. I have a feeling the answer is implicit conversion, but I can't figure out how to implicitly convert something to a trait, which is an abstract type.
Here's what I've tried so far:
trait Appendable[T, C] {
def :+(other: T): C
}
case class CustomContainer[T](data: List[T]) extends Appendable[T, CustomContainer[T]] {
override def :+(other: T): CustomContainer[T] = copy(data = data :+ other)
}
implicit def listToAppendable[T](list: List[T]): Appendable[T, List[T]] = ??? // What goes here?
def append[T, A <: Appendable[T, A]](appendable: A, other: T): A = appendable :+ other
append(List(1,2,3), 4) // List(1,2,3,4)
append(CustomContainer(List(1,2,3)), 4) // CustomContainer(List(1,2,3,4))
What goes in the ??? spot? Alternatively is there a better way to do this?
I am on Scala 2.10.4.
You can use a type class for this:
final case class CustomContainer[T](data: List[T])
trait Appendable[T, C[_]] {
def specialAppend(other: T, acc: C[T]): C[T]
}
object Appendable {
implicit def listAppendable[A]: Appendable[A, List] = new Appendable[A, List] {
override def specialAppend(other: A, acc: List[A]): List[A] = other :: acc
}
implicit def customContainerAppendable[A]: Appendable[A, CustomContainer] = new Appendable[A, CustomContainer] {
override def specialAppend(other: A, acc: CustomContainer[A]): CustomContainer[A] = acc.copy(data = other :: acc.data)
}
}
object Foo {
implicit class AppendableOps[A, C[_]](val c: C[A]) {
def :+!(elem: A)(implicit appendable: Appendable[A, C]): C[A] = appendable.specialAppend(elem, c)
}
def main(args: Array[String]): Unit = {
println(List(1,2,3,4) :+! 6)
println(CustomContainer(List(1)) :+! 2)
()
}
}
Yields:
List(6, 1, 2, 3, 4)
CustomContainer(List(2, 1))

Cannot get type of generic object in a list

I have the following trait:
trait Storage[C <: Config] {
def get(name: String, version: Int): Option[C]
def list: List[(String, String)]
def register(config: C): Boolean
}
and I want to create the following class:
class MultiStorage[C <: Config](storages: List[Storage[_ <: C]]) extends Storage[C] {
def get(name: String, version: Int): Option[C] = {...}
def list: List[(String, String)] = {...}
def register(config: C) = {...}
If not clear, the meaning is that a MultiStorage stores elements of type C (or subtype) in several storages, each of them containing elements of a single type.
I am fighting with generics to implement the register method. The idea is that depending on the type of object I want to register, I need to choose the right storage to register on, something like:
def register(config: C) = {
storages.foreach(s => {
if (typeOf(s) is Storage[C]) { // same type of config
s.register(config)
return
}
})
}
I made several attempts with generics and type tags, however nothing useful to share here. I am probably thinking I need to add another type tag to distinguish what I receive in register and what is declared as type of the storage.
One of the ideas I tried was having a method in Storage that returns the type:
protected def getType()(implicit tag: TypeTag[C]): universe.Type = typeOf[C]
but on the caller side I was able to get as result something like _$1, which I honestly don't understand what it means.
Another try was to use shapeless, but in this case I am not sure if it is possible having a multi-storage containing an arbitrary number of elements in an HList storage
The runtime class needs to be made available, for example:
class Config
class FooConfig extends Config
class BarConfig extends Config
trait Storage[C <: Config] {
val ctag: ClassTag[C]
def get(name: String, version: Int): Option[C]
def list: List[(String, String)]
def register(config: C): Boolean
}
class FooStorage(implicit val ctag: ClassTag[FooConfig]) extends Storage[FooConfig] {
override def get(name: String, version: Int): Option[FooConfig] = ???
override def list: List[(String, String)] = ???
override def register(config: FooConfig): Boolean = ???
}
class BarStorage(implicit val ctag: ClassTag[BarConfig]) extends Storage[BarConfig] {
override def get(name: String, version: Int): Option[BarConfig] = ???
override def list: List[(String, String)] = ???
override def register(config: BarConfig): Boolean = ???
}
class MultiStorage[C <: Config](storages: List[Storage[_ <: C]])(implicit val ctag: ClassTag[C]) extends Storage[C] {
def get(name: String, version: Int): Option[C] = ???
def list: List[(String, String)] = ???
def register(config: C): Boolean = {
storages.foreach(storage => {
if (storage.ctag.runtimeClass.isAssignableFrom(config.getClass)) {
}
})
???
}
}
As traits can't have constructor parameters, the implicit class tag needs to be repeated in every class implementing the trait. If your class structure allows Storage to be an abstract class instead, the amount of boilerplate can be reduced:
abstract class Storage[C <: Config](implicit val ctag: ClassTag[C]) {
def get(name: String, version: Int): Option[C]
def list: List[(String, String)]
def register(config: C): Boolean
}
class FooStorage extends Storage[FooConfig] {
override def get(name: String, version: Int): Option[FooConfig] = ???
override def list: List[(String, String)] = ???
override def register(config: FooConfig): Boolean = ???
}
Possible approach with Shapeless is
import shapeless.{::, HList, HNil}
object App {
trait Config
object config1 extends Config
object config2 extends Config
trait Storage[C <: Config] {
def get(name: String, version: Int): Option[C]
def list: List[(String, String)]
def register(config: C): Boolean
}
object storage1 extends Storage[config1.type] {
override def get(name: String, version: Int): Option[config1.type] = ???
override def list: List[(String, String)] = ???
override def register(config: config1.type): Boolean = {
println("storage1#register")
true
}
}
object storage2 extends Storage[config2.type] {
override def get(name: String, version: Int): Option[config2.type] = ???
override def list: List[(String, String)] = ???
override def register(config: config2.type): Boolean = {
println("storage2#register")
true
}
}
class MultiStorage[L <: HList](storages: L) /*extends Storage[C]*/ {
// def get(name: String, version: Int): Option[C] = ???
def list: List[(String, String)] = ???
def register[C <: Config](config: C)(implicit find: Find[C, L]): Boolean = find(config, storages).register(config)
}
trait Find[C <: Config, L <: HList] {
def apply(config: C, l: L): Storage[C]
}
trait LowPriorityFind {
implicit def tail[C <: Config, L <: HList, C1 <: Config, T <: HList](implicit
ev: L <:< (Storage[C1] :: T),
find: Find[C, T]): Find[C, L] = (config, l) => find(config, l.tail)
}
object Find extends LowPriorityFind {
implicit def head[C <: Config, L <: HList, T <: HList](implicit
ev: L <:< (Storage[C] :: T)): Find[C, L] = (_, l) => l.head
}
val multiStorage = new MultiStorage(storage1 :: storage2 :: HNil)
def main(args: Array[String]): Unit = {
multiStorage.register(config1) // storage1#register
multiStorage.register(config2) // storage2#register
}
}
Making MultiStorage extend Storage can be too restrictive. We could write
class MultiStorage(storages: List[Storage[_ <: Config]] /*i.e. List[Storage[T] forSome { type T <: Config}]*/)
extends Storage[T forSome { type T <: Config }] /*i.e. just Storage[Config]*/
i.e. Storage of some unknown type T <: Config. But since we can register configs of any type T <: Config it should be more like Storage[T forAll { type T <: Config }] or Storage[[T <: Config]T] if such syntax existed in Scala but actually Scala doesn't have rank-2 types.

Is there a way to guarantee case class copy methods exist with type classes in Scala?

In the example below I have a type class Foo, and would like to somehow guarantee that all members conforming to Foo (such as Bar via barFoo) have a copy method such as the one generated by way of being a case class. I haven't thought of a way to do this. In this case the copy signature would be possibly be something like copy(foo: F, aa: List[T] = foo.aa, maybe: Option[T] = foo.maybe): F.
trait Foo[F] {
type T
def aa(foo: F): List[T]
def maybe(foo: F): Option[T]
}
final case class Bar(aa: List[String], maybe: Option[String])
object Bar {
implicit val barFoo = new Foo[Bar] {
type T = String
def aa(foo: Bar): List[String] = foo.aa
def maybe(foo: Bar): Option[T] = foo.maybe
}
}
I couldn't do it with type member, but here you are a version with type parameters. Also it is necessary to add a method to Foo to construct the object.
trait Foo[F, T] {
def aa(foo: F): List[T]
def maybe(foo: F): Option[T]
def instance(aa: List[T], maybe: Option[T]): F
}
class Bar(val aa: List[String], val maybe: Option[String]) {
override def toString = s"Bar($aa, $maybe)"
}
object Bar {
implicit val barFoo = new Foo[Bar, String] {
def aa(foo: Bar): List[String] = foo.aa
def maybe(foo: Bar): Option[String] = foo.maybe
def instance(aa: List[String], maybe:Option[String]):Bar = new Bar(aa, maybe)
}
}
implicit class FooOps[A, T](fooable:A)(implicit foo:Foo[A, T]) {
def copy(aa: List[T] = foo.aa(fooable), maybe: Option[T] = foo.maybe(fooable)) = {
foo.instance(aa, maybe)
}
}
val r = new Bar(List(""), Option("")).copy(aa = List("asd"))
println(r)

Value class for type class with multiple type parameters

It is a common practice to provide a helper value class for accessing type classes - something like
object Show {
def apply[A](implicit sh: Show[A]): Show[A] = sh
def show[A: Show](a: A) = Show[A].show(a)
implicit class ShowOps[A: Show](a: A) {
def show = Show[A].show(a)
}
implicit val intCanShow: Show[Int] =
new Show[Int] {
def show(int: Int): String = s"int $int"
}
}
In my case, I have a type class with 2 parameters. And I cannot get the value class to work (MapperOps). I keep getting "could not find implicit value for parameter mapper"
trait Mapper[T, D] {
def toDto(i: T): D
}
object Mapper {
def map[T, D](i: T)(implicit mapper: Mapper[T, D]): D = implicitly[Mapper[T, D]].toDto(i)
def apply[T, D](implicit mapper: Mapper[T, D]): Mapper[T, D] = mapper
implicit def optionMapper[T, D](implicit mapper: Mapper[T, D]): Mapper[Option[T], Option[D]] = {
new Mapper[Option[T], Option[D]] {
override def toDto(i: Option[T]): Option[D] = i.map(mapper.toDto)
}
}
implicit def seqMapper[T, D](implicit mapper: Mapper[T, D]): Mapper[Seq[T], Seq[D]] = {
new Mapper[Seq[T], Seq[D]] {
override def toDto(i: Seq[T]): Seq[D] = i.map(mapper.toDto)
}
}
// Neither works
implicit class MapperOps1[T,D](a: T) {
def toDto = Mapper[T,D].toDto(a)
}
implicit class MapperOps2[T,D](a: T) {
def toDto(implicit mapper: Mapper[T,D]) = Mapper[T,D].toDto(a)
}
implicit class MapperOps3[T,D](a: T) {
def toDto[D](implicit mapper: Mapper[T,D]): D Mapper[T,D].toDto(a)
}
}
Notice the important difference that in
implicit class ShowOps[A: Show](a: A) { ... }
you have this A : Show part, which essentially desugares into
implicit class ShowOps[A](a: A)(implicit s: Show[A]) { ... }
Since there is no neat [T : MyTypeClass]-syntax for two-parameter "Type-Pair-Classes", you have to provide the implicit Mapper as a separate
parameter to the constructor:
implicit class MapperOps1[T, D](a: T)(implicit m: Mapper[T, D]) {
def toDto = Mapper[T, D].toDto(a)
}
The following little test compiles and outputs "42":
implicit object IntToStringMapper extends Mapper[Int, String] {
def toDto(i: Int): String = i.toString
}
import Mapper._
val s: String = 42.toDto
println(s)
Alternatively, you could try it with an implicit conversion (compiler will whine at you if you don't enable this feature explicitly, and users will whine at you if you don't use this feature wisely):
class MapperOps1[T,D](a: T, mapperTD: Mapper[T, D]) {
def toDto = {
implicit val m: Mapper[T, D] = mapperTD
Mapper[T,D].toDto(a)
}
}
import scala.language.implicitConversions
implicit def wrapIntoMapperOps1[T, D]
(a: T)
(implicit m: Mapper[T, D]): MapperOps1[T, D] = new MapperOps1(a, m)
I won't comment on your two other attempts: apparently, the compiler cannot instantiate those, because it doesn't get enough information about the type parameters before it has to instantiate a wrapper.