How to implement typesafe domain repository in scala? - scala

I want to implement generic and typesafe domain repository. Say I have
trait Repo[Value] {
def put(value: Value): Unit
}
case class IntRepo extends Repo[Int] {
override def put(value: Int): Unit = ???
}
case class StringRepo extends Repo[String] {
override def put(value: String): Unit = ???
}
case class DomainRepo(intRepo: IntRepo, stringRepo: StringRepo) {
def putAll[?](values: ?*): Unit // what type should be here?
}
As result I want to have following api:
domainRepo.putAll(1, 2, 3, "foo", "bar") //Should work
domainRepo.putAll(1, 2, true, "foo") // should not compile because of boolean value
The question is How to achieve this?
so, I see only one way to make it typesafe. It's to do pattern matching on Any type like
def putAll(values: Seq[Any]) => Unit = values.foreach {
case str: String => stringRepo.put(str)
case int: Int => intRepo.put(int)
case _ => throw RuntimeException // Ha-Ha
}
but what if I would have 10000 of types here? it would be a mess!
another not clear for me approach for now is to use dotty type | (or) like following:
type T = Int | String | 10000 other types // wouldn't be a mess?
def putAll(t: T*)(implicit r1: Repo[Int], r2: Repo[String] ...) {
val myTargetRepo = implicitly[Repo[T]] // would not work
}
so, what do you think? is it even possible?
the easies way I've saw was
Map[Class[_], Repo[_]]
but this way allows to do a lot of errors

It seems you are looking for a type class
trait Repo[Value] {
def put(value: Value): Unit
}
implicit val intRepo: Repo[Int] = new Repo[Int] {
override def put(value: Int): Unit = ???
}
implicit val stringRepo: Repo[String] = new Repo[String] {
override def put(value: String): Unit = ???
}
case object DomainRepo {
def putAll[Value](value: Value)(implicit repo: Repo[Value]): Unit = repo.put(value)
}
If you want domainRepo.putAll(1, 2, 3, "foo", "bar") to compile and domainRepo.putAll(1, 2, true, "foo") not to compile, you can try to use heterogeneous collection (HList).
import shapeless.{HList, HNil, ::, Poly1}
import shapeless.ops.hlist.Mapper
trait Repo[Value] {
def put(value: Value): Unit
}
implicit val intRepo: Repo[Int] = new Repo[Int] {
override def put(value: Int): Unit = ???
}
implicit val stringRepo: Repo[String] = new Repo[String] {
override def put(value: String): Unit = ???
}
case object DomainRepo {
def put[Value](value: Value)(implicit repo: Repo[Value]): Unit = repo.put(value)
object putPoly extends Poly1 {
implicit def cse[Value: Repo]: Case.Aux[Value, Unit] = at(put(_))
}
def putAll[Values <: HList](values: Values)(implicit
mapper: Mapper[putPoly.type, Values]): Unit = mapper(values)
}
DomainRepo.putAll(1 :: 2 :: 3 :: "foo" :: "bar" :: HNil)
// DomainRepo.putAll(1 :: 2 :: true :: "foo" :: HNil) // doesn't compile

Related

How to pattern match all classes with context bound

I have a type class and a few instances:
trait TC[T] { def doThings(x: T): Unit }
implicit val tcA = new TC[A] { /* ... */}
implicit val tcB = new TC[B] { /* ... */}
implicit val tcC = new TC[C] { /* ... */}
/* ... */
In my call site, I have input as Any, and I need to check if there is an implicit for the input actual type:
def process(in: Any) = in match {
case x: A => implicitly[TC[A]].doThings(x)
case x: B => implicitly[TC[B]].doThings(x)
case x: C => implicitly[TC[C]].doThings(x)
//...
}
This seems tedious and unnecessary, as I have to list all the classes that have this type class instance. Can I achieve this by something like:
def process(in: Any) = in match {
case x: T : TC => implicitly[TC[T]].doThings(x) //This does not work
}
Edit: input is an Any (an Object from a Java library). Cannot use generic or context bound on the input.
If you really want to do what you have mentioned in your question, you can write it as below, but if you just want to call doThings by finding an implicit instance of appropriate TC - refer João Guitana answer
object Main extends App {
class A
class B
class C
trait TC[T] { def doThings(x: T): Unit }
implicit val tcA = new TC[A] {
override def doThings(x: A): Unit = println("From A")
}
implicit val tcB = new TC[B] {
override def doThings(x: B): Unit = println("From B")
}
implicit val tcC = new TC[C] {
override def doThings(x: C): Unit = println("From C")
}
def process[T: ClassTag](in: T) = in match {
case x: A => implicitly[TC[A]].doThings(x)
case x: B => implicitly[TC[B]].doThings(x)
case x: C => implicitly[TC[C]].doThings(x)
}
process(new A())
process(new B())
process(new C())
}
/* === Output ====
From A
From B
From C
*/
You need to ask for an implicit TC, Any won't work. As follows:
trait TC[T] { def doThings(x: T): Unit }
implicit def tcS: TC[String] = new TC[String] {
override def doThings(x: String): Unit = println("string")
}
implicit def tcI: TC[Int] = new TC[Int] {
override def doThings(x: Int): Unit = println("int")
}
def process[T : TC](x: T): Unit = implicitly[TC[T]].doThings(x)
process("")
process(1)
// process(4L) wont compile
Try it out!

In Scala, how to deal with heterogeneous list of the same parameterized type

I have an array of Any (in real life, it's a Spark Row, but it's sufficient to isolate the problem)
object Row {
val buffer : Array[Any] = Array(42, 21, true)
}
And I want to apply some operations on its elements.
So, I've defined a simple ADT to define a compute operation on a type A
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
Given that I have a list of all operations and I know which operation is to apply to each element, let's use these operations.
object GenericsOp {
import Row._
val ops = Seq(Count, Exist)
def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}
By design, for a given op, types are aligned between cast and combine. But unfortunately the following code does not compile. The error is
Type mismatch, expected: _$1, actual: AnyVal
Is there a way to make it work ?
I've found a workaround by using abstract type member instead of type parameter.
object AbstractOp extends App {
import Row._
trait Op {
type A
def compute(a: A) : A
}
case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}
val ops = Seq(Count, Exist)
def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}
Is there a better way ?
It seems that your code can be simplified by making Op[A] extend Any => A:
trait Op[A] extends (Any => A) {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
def apply(a: Any): A = compute(cast(a))
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i)(buffer(i))
}
println(buffer.mkString("[", ",", "]"))
}
}
Since it's asInstanceOf everywhere anyway, it does not make the code any less safe than what you had previously.
Update
If you cannot change the Op interface, then invoking cast and compute is a bit more cumbersome, but still possible:
trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}
case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}
case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}
object AbstractOp {
val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)
def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i) match {
case op: Op[t] => op.compute(op.cast(buffer(i)))
}
}
println(buffer.mkString("[", ",", "]"))
}
}
Note the ops(i) match { case op: Opt[t] => ... } part with a type-parameter in the pattern: this allows us to make sure that cast returns a t that is accepted by compute.
As a more general solution than Andrey Tyukin's, you can define the method outside Op, so it works even if Op can't be modified:
def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))
buffer(0) = apply(ops(0), buffer(0))

How instantiate case class from Map[String, String]

Lets say I have
case class Sample(i:Int, b:Boolean)
and
Map[String, String]("i" => "1", "b" => "false")
What is most concise way to instantiate any case class(if all fields are in map), signature like this:
get[T](map:[String, String]) : T
Probably shapeless can help acomplish this task, but I am almost unfamiliar with it. Thanks!
Firstly you can transform Map[String, String] into Map[Symbol, Any] and then use type classes shapeless.ops.maps.FromMap (or extention method .toRecord) and LabelledGeneric:
import shapeless.LabelledGeneric
import shapeless.record.Record
import shapeless.syntax.std.maps._
case class Sample(i: Int, b: Boolean)
val rec = Map('i -> 1, 'b -> false).toRecord[Record.`'i -> Int, 'b -> Boolean`.T].get
LabelledGeneric[Sample].from(rec) //Sample(1,false)
If not using Shapeless is an option for you, you could do it in a simple way without it. Here is a sample implementation where I use the Try Monad and a pattern matching to transform the Map into your case class:
(Try("1".toInt), Try("false".toBoolean)) match {
case (Success(intVal), Success(boolVal)) =>
Sample(intVal, boolVal)
case _ => // Log and ignore the values
}
Of course this is a bit verbose than the Shapeless version, but if you do not want to use a full library just for this simple use case, you could always do it using Scala library!
guys. Probably I wasn't too concise. What I really need is Generic parser from Map of Strings to case class. Thanks for pointing out to LabelledGeneric. Here is solution:
import shapeless.labelled.{FieldType, field}
import shapeless.{::, HList, HNil, LabelledGeneric, Lazy, Witness}
object FieldParser {
trait ValParser[A] {
def parse(str:String): A
}
trait FieldParser[A] {
def parse: A
}
type FT[A, B] = FieldType[A, B]
type ListField[K <: Symbol, A] = FieldType[K, List[A]]
type FP[A] = FieldParser[A]
type Args = (List[String],Map[String, Int])
type Named[K <: Symbol] = Witness.Aux[K]
private def create[A](thunk: A): FP[A] = {
new FP[A] {
def parse: A = thunk
}
}
def apply[A](implicit st: Lazy[FP[A]]): FP[A] = st.value
implicit def genericParser[A, R <: HList](implicit generic: LabelledGeneric.Aux[A, R], parser: Lazy[FP[R]], args:Args): FP[A] = {
create(generic.from(parser.value.parse))
}
implicit def hlistParser[K <: Symbol, H, T <: HList](implicit hParser: Lazy[FP[FT[K, H]]], tParser: FP[T]): FP[FT[K, H] :: T] = {
create {
val hv = hParser.value.parse
val tv = tParser.parse
hv :: tv
}
}
implicit def standardTypeParser[K <: Symbol, V:ValParser](implicit named: Named[K], args:Args): FP[FieldType[K, V]] = {
create(field[K](implicitly[ValParser[V]].parse(findArg)))
}
implicit def optionParser[V](implicit valParser:ValParser[V]): ValParser[Option[V]] = new ValParser[Option[V]]{
def parse(str:String):Option[V] = {
str.isEmpty match {
case true => None
case false => Some(valParser.parse(str))
}
}
}
implicit def listParser[V](implicit valParser:ValParser[V]): ValParser[List[V]] = new ValParser[List[V]]{
def parse(str:String):List[V] = {
str.isEmpty match {
case true => Nil
case false => str.split(",").map(valParser.parse).toList
}
}
}
implicit def doubleParser: ValParser[Double] = new ValParser[Double]{
def parse(str:String):Double = str.toDouble
}
implicit def intParser: ValParser[Int] = new ValParser[Int]{
def parse(str:String):Int = str.toInt
}
implicit def strParser: ValParser[String] = new ValParser[String]{
def parse(str:String):String = str
}
implicit def boolParser: ValParser[Boolean] = new ValParser[Boolean]{
def parse(str:String):Boolean = str.toBoolean
}
implicit val hnilParser: FP[HNil] = create(HNil)
private def findArg[K <: Symbol](implicit args:Args, named: Named[K]): String = {
val name = named.value.name
val index = args._2(name)
args._1(index)
}
}

Combine two functions under the same name without overloading

Here is the code:
def transform1(f: String => String): Unit = {
val s = getString
f.andThen(putString)(s)
}
def transform2(f: String => Option[String]): Unit = {
val s = getString
f(s).foreach(putString(_))
}
How do you express these two ideas in one single function?
Method overloading does not work and seems discouraged by the community.
I didn't understand that why anyone may want this but here is a way to do it:
def transform(f: Either[(String => String), (String => Option[String])]: Unit = f match {
case Left(f) => // do transform1 here
case Right(f) => //do transform2 here
}
As I said at the begining you probably shouldn't want to do this; perhaps you should directly ask what you want.
The pattern to avoid overloading is to convert disparate arguments to a common, specific type. There could be any number of such conversions.
Not sure this is the most compelling example, however.
object X {
trait MapFlat[-A, +B] { def apply(x: A): B }
implicit class mapper[A](val f: A => A) extends MapFlat[A, A] {
override def apply(x: A) = {
val res = f(x)
println(res)
res
}
}
implicit class flatmapper[A](val f: A => Option[A]) extends MapFlat[A, Option[A]] {
override def apply(x: A) = {
val res = f(x)
res foreach println
res
}
}
def f[B](g: MapFlat[String, B]) = {
g("abc")
}
}
object Test extends App {
import X._
f((s: String) => s)
f((s: String) => Some(s))
}
One way to do it will be type classes, here's a sample -
trait Transformer[T] {
def transform(foo: String => T)
}
object Transformer {
implicit object StringTransformer extends Transformer[String] {
override def transform(foo: (String) => String): Unit = ??? // Your logic here
}
implicit object OptStringTransformer extends Transformer[Option[String]] {
override def transform(foo: (String) => Option[String]): Unit = ??? // Your logic here
}
}
class SampleClass {
def theOneTransformYouWant[T: Transformer](f: String => T) = {
implicitly[Transformer[T]].transform(f)
}
def canUseBothWays(): Unit = {
theOneTransformYouWant((s: String) => s)
theOneTransformYouWant((s: String) => Some(s))
}
}
Another way would be the magnet pattern
http://spray.io/blog/2012-12-13-the-magnet-pattern/
sealed trait TransformationMagnet {
def apply(): Unit
}
object TransformationMagnet {
implicit def fromString(f: String => String): TransformationMagnet =
new TransformationMagnet {
def apply(): Unit = ??? // Your code goes here
}
implicit def fromOptString(f: String => Option[String]): TransformationMagnet =
new TransformationMagnet {
def apply(): Unit = ??? // your code goes here
}
}
class SampleClass {
def theOneTransformYouWant(f: TransformationMagnet) = {
???
}
def hereWeUseItInBothWays(): Unit = {
theOneTransformYouWant((s: String) => s)
theOneTransformYouWant((s: String) => Some(s))
}
}
add a new parameter on the description typeOfTransform
add a conditional inside the function
if (typeOfTransform == type1){
//functionality1
}else {
//functionality2
}
Just for completeness, you can actually overload methods like this by adding implicit arguments which will always be available:
def transform(f: String => Option[String]): Unit = ...
def transform(f: String => String)(implicit d: DummyImplicit): Unit = ...

Can a Scala "extractor" use generics on unapply?

Can't I use a generic on the unapply method of an extractor along with an implicit "converter" to support a pattern match specific to the parameterised type?
I'd like to do this (Note the use of [T] on the unapply line),
trait StringDecoder[A] {
def fromString(string: String): Option[A]
}
object ExampleExtractor {
def unapply[T](a: String)(implicit evidence: StringDecoder[T]): Option[T] = {
evidence.fromString(a)
}
}
object Example extends App {
implicit val stringDecoder = new StringDecoder[String] {
def fromString(string: String): Option[String] = Some(string)
}
implicit val intDecoder = new StringDecoder[Int] {
def fromString(string: String): Option[Int] = Some(string.charAt(0).toInt)
}
val result = "hello" match {
case ExampleExtractor[String](x) => x // <- type hint barfs
}
println(result)
}
But I get the following compilation error
Error: (25, 10) not found: type ExampleExtractor
case ExampleExtractor[String] (x) => x
^
It works fine if I have only one implicit val in scope and drop the type hint (see below), but that defeats the object.
object Example extends App {
implicit val intDecoder = new StringDecoder[Int] {
def fromString(string: String): Option[Int] = Some(string.charAt(0).toInt)
}
val result = "hello" match {
case ExampleExtractor(x) => x
}
println(result)
}
A variant of your typed string decoder looks promising:
trait StringDecoder[A] {
def fromString(s: String): Option[A]
}
class ExampleExtractor[T](ev: StringDecoder[T]) {
def unapply(s: String) = ev.fromString(s)
}
object ExampleExtractor {
def apply[A](implicit ev: StringDecoder[A]) = new ExampleExtractor(ev)
}
then
implicit val intDecoder = new StringDecoder[Int] {
def fromString(s: String) = scala.util.Try {
Integer.parseInt(s)
}.toOption
}
val asInt = ExampleExtractor[Int]
val asInt(Nb) = "1111"
seems to produce what you're asking for. One problem remains: it seems that trying to
val ExampleExtractor[Int](nB) = "1111"
results in a compiler crash (at least inside my 2.10.3 SBT Scala console).