I have a controller
def getCars(notation: Option[Boolean] = Some(false)) = identified.auth(hasOceanScope).async { implicit request =>
carService.getCars().map {
case Seq() => Response.NotFound
case cars => Response.Ok(cars)
}
}
Car case class looks like this:
case class Car(
name: String,
createdAt: LocalDateTimeOffset,
wheels: Seq[Wheel]
)
object Car{
implicit val wheelFormat = Wheel.format
implicit def toOffset(date: LocalDateTime): LocalDateTimeOffset = LocalDateTimeOffset.apply(date)
implicit val format = Json.format[Car]
case class Wheel(
name: String,
createdAt: LocalDateTimeOffset
)
object Wheel{
implicit val format = Json.format[Wheel]
implicit def toOffset(date: LocalDateTime): LocalDateTimeWithOffset = LocalDateTimeWithOffset.apply(date)
)
When notation query parameter is true -> want to return createdAt Car object and Wheel object field with notation => 2022-10-22T00:00:00#1
When notation query parameter is false -> want to return createdAt Car object and Wheel object field without notation => 2022-10-22T00:00:00
That is why I have create two formats in LocalDateTimeOffset object
case class LocalDateTimeWithOffset(dt: LocalDateTime, offset: Int) {
val localDateTimeWithOffsetReads: Reads[LocalDateTimeWithOffset] = Reads.of[String].flatMap {
str => if (str.contains("#")) {
val (dt, offset) = str.splitAt(str.indexOf("#"))
Reads.pure(LocalDateTimeWithOffset(LocalDateTime.parse(dt), offset.drop(1).toInt))
} else {
Reads.pure(LocalDateTimeWithOffset(LocalDateTime.parse(str), 1))
}
}
val localDateTimeWithOffsetWrites: Writes[LocalDateTimeWithOffset] = new Writes[LocalDateTimeWithOffset] {
override def writes(a: LocalDateTimeWithOffset): JsValue = JsString(a.dt.format(dateTimeUTCFormatter) + s"#${a.offset}")
}
val localDateTimeWithOffsetWritesOff: Writes[LocalDateTimeWithOffset] = new Writes[LocalDateTimeWithOffset] {
override def writes(a: LocalDateTimeWithOffset): JsValue = JsString(a.dt.format(dateTimeUTCFormatter))
}
val localDateTimeWithoutOffsetFormat: Format[LocalDateTimeWithOffset] = Format(localDateTimeWithOffsetReads, localDateTimeWithOffsetWritesOff)
val localDateTimeWithOffsetFormat: Format[LocalDateTimeWithOffset] = Format(localDateTimeWithOffsetReads, localDateTimeWithOffsetWrites)
implicit var format: Format[LocalDateTimeWithOffset] = localDateTimeWithoutOffsetFormat
}
But how can I use two different formats from controller based on notation query parameter value?
Well just looking at your question's title, changing implicit value is not something you would see Scala developers do, because compiler is responsible to lookup for implicit values, and you would definitely want to avoid ambiguous implicits found error. instead, you see developers using something so called type class instance constructor or something similar. This is how it works in your case:
Assuming you have a class A, which can be formatted to/from Json in many ways:
case class A(field1: String) // could have more fields
object A {
val formatFirstApproach: Format[A] = ???
val formatSecondApproach: Format[A] = ???
// note that the above instances are not implicit
def getFormat(somePredicate: Boolean): Format[A] = {
// input parameters can be anything, these are the parameters you need,
// in order to be able to decide which instance to return
if (somePredicate) formatFirstApproach else formatSecondApproach
}
}
And then, given a class B which has an instance variable of type A in it, you can use the type class instance constructor:
case class B(a: A, field2: Int)
object B {
// this is the type class instance constructor, since it constructs an instance of a type class (Format in this case)
implicit def format(implicit aFormatter: Format[A]): Format[B] = Json.format
}
And the thing is, you probably would not care about the serialization unless in the controller layer, so in the controller layer you can do:
def someApi(flag: Boolean) = Action async { req =>
implicit val aFormatter = A.getFormat(flag) // that's it, you don't need to mention anything more anywhere
businessLogic().map {
case Seq() => Response.NotFound
case cars => Response.Ok(Json.toJson(cars))
}
}
Related
I am trying to create a factory pattern, and the return type is a parametrized trait, and the actual return type will be a subtype of that trait, but I do not know the generic type beforehand, so I don't know how to specify the return type. A simplified version is like this:
trait Mapper[T] {
def createMap(a: T, b: T): T
}
class MapperA extends Mapper[String]{
override def createMap(a: String, b: String): String = {
a + b
}
}
class MapperB extends Mapper[Int]{
override def createMap(a: Int, b: Int): Int = {
a + b + b + b
}
}
def factory(typeOfMapper: String): Mapper = {
typeOfMapper match {
case "mapperA" => new MapperA()
case "mapperB" => new MapperB()
}
}
val mapper = factory("mapperA")
This will give me and error of
trait Mapper takes type parameters
but I do not know the generic type beforehand. What should the return type be for the factory method here?
Well, you could return Mapper[_] ... that will compile, but isn't really very useful, as pointed out in the comments: you won't be able to use the returned value in any meaningful way, because actual type of create will be unknown.
If different instances of your mapper will always have different type parameters (at least, within the same scope), then a neat solution would be using implicits:
object Mapper {
implicit def strMapper: Mapper[String] = new MapperA
implicit def intMapper: Mapper[Int] = new MapperB
}
Then everywhere you have these visible, you can just do:
val m1: Mapper[String] = implicitly[Mapper[String]]
val m2: Mapper[Int] = implicitly[Mapper[Int]]
You could also write your factory function (though, I am not sure why you'd want to) like this:
def mapperFactory[T: Mapper] = implicitly[Mapper[T]]
and use it like this:
val m: Mapper[String] = mapperFactory
or like this
def intMapper = mapperFactory[Int]
If you want different mappers for the same type parameter, it's basically the same idea, except it does't look as neat without implicits. The key is different factories for different types:
class Mapper {
def str(`type`: String): Mapper[String] = `type` match {
case "foo" => FooMapper()
case "bar" => BarMapper()
}
def int(`type`: String): Mapper[Int] = `type` match {
case "baz" => BazMapper()
case "bak" => BakMapper()
}
}
val fooMapper = Mapper.str("foo")
val bakMapper = Mapper.int("bak")
I have a lot of classes such as DataFrameFlow, TextFlow, RDDFlow. They all derive from base class Flow.
Now I want to write a function judgeFlow which can read from a path: String and return something representing exact Flow type from which I can create corresponding instance. The whole code seems like the following
def judgeFlow(path:String) = /*1*/ {
Flow.getStoreType(path) match {
case StoreType.tdw =>
DataFrameFlow
case StoreType.hdfs =>
TextFlow
}
}
def createFlow(typeInfo:/*2*/) = /*3*/{
new typeInfo()
}
However, I don't know how to write in place 1, 2 and 3.
EDIT
Knowing how to construct them is not enough here, because I also want the following:
pattern matching through typeInfo
some ways to do asInstanceOf
EDIT 2
Definition of Flow
abstract class Flow(var outputName: String) extends Serializable{
def this() = this("")
...
}
Definition of DataFrameFlow
class DataFrameFlow(d: DataFrame, path: String) extends Flow {
var data: DataFrame = d
def this(data: DataFrame) = this(data, "")
def this(path: String) = this(null, path)
def this() = this(null, "")
...
}
Pattern matching can't return different types from different cases. The type returned by pattern matching is the least upper bound of types returned in cases.
When someone wants to return different types, most probably he/she wants a type class.
sealed abstract class Flow
class DataFrameFlow extends Flow
class TextFlow extends Flow
class RDDFlow extends Flow
trait JudgeFlow[In] {
type Out <: Flow
def judgeFlow(in: In): Out
}
object JudgeFlow {
implicit val `case1`: JudgeFlow[???] { type Out = DataFrameFlow } = ???
implicit val `case2`: JudgeFlow[???] { type Out = TextFlow } = ???
implicit val `case3`: JudgeFlow[???] { type Out = RDDFlow } = ???
}
def judgeFlow[In](in: In)(implicit jf: JudgeFlow[In]): jf.Out = jf.judgeFlow(in)
But the trouble is that types are resolved at compile time. You seem to want to choose a case based on a value of string i.e. at runtime. So you can't return more specific types than just Flow at compile time.
flatMap with Shapeless yield FlatMapper not found
It's hard to guess your use case completely.
But using Scala reflection you can try
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
def judgeFlow(path:String): Type = {
Flow.getStoreType(path) match {
case StoreType.tdw =>
typeOf[DataFrameFlow]
case StoreType.hdfs =>
typeOf[TextFlow]
}
}
def createFlow(typeInfo: Type): Flow = {
val constructorSymbol = typeInfo.decl(termNames.CONSTRUCTOR).asMethod
val classSymbol = typeInfo.typeSymbol.asClass
val classMirror = currentMirror.reflectClass(classSymbol)
val constructorMirror = classMirror.reflectConstructor(constructorSymbol)
constructorMirror().asInstanceOf[Flow]
}
I have different sources and corresponding parameters
Source1, Source2, Source3
Parameter1, Parameter2, Parameter3,
Source: is trait (can be changed)
trait Source[T] {
def get(Parameter)(implicit c: Context): MyData[T]
}
Parameter is also a trait
trait Parameter
I have different OutputType class: T1, T2, T3
I need output as: MyData[OutputType]
Fixed API signature (changes to the signature not quite preferable):
val data1: MyData[T1] = MyAPI.get[T1](Parameter1("a", "b")) // this should give MyData from Source1 of type T1
val data2: MyData[T2] = MyAPI.get[T2](Parameter3(123)) // this should give MyData from Source3 of type T2
Some source supports some output types (say T1, T2), but some may not.
What I did:
I tried using scala reflection typeTag to determine the type at runtime, but since return type will be MyData[T], and is in contra-variant position, it wont know the actual return type. (Why does TypeTag not work for return types?)
e.g.
object MyAPI {
get[T: TypeTag](p: Parameter)(implicit c: Context): MyData[T] = {}
}
I also tried using type-class pattern. Scala TypeTag Reflection returning type T
I can work with different OutputType creating implicit val for each, but would only work for single Source1. I can't manage to work for all sources.
I was trying to do:
object MyAPI {
get[T: SomeConverter](p: Parameter)(implicit c: Context): MyData[T] = {
p match {
case Parameter1 => Source1[T].read(p.asInstanceOf(Parameter1)
case Parameter2 => Source2[T].read(p.asInstanceOf(Parameter2)
}
}
}
Disclaimer: I think I figured out what you want. I'm also learning to design type-safe APIs, so here's one.
Provided variant uses implicits. You have to manually establish mapping between parameter types and results they yield, which may or may not include sources. It does not work on runtime, however, so I also removed common trait Parameter. It also does not impose any restrictions on the Sources at all.
It also "looks" the way you wanted it to look, but it's not exactly that.
case class User(id: Int) // Example result type
// Notice I totally removed any and all relation between different parameter types and sources
// We will rebuild those relations later using implicits
object Param1
case class Param2(id: Int)
case class Param3(key: String, filter: Option[String])
// these objects have kinda different APIs. We will unify them.
// I'm not using MyData[T] because it's completely irrelevant. Types here are Int, User and String
object Source1 {
def getInt = 42
}
object Source2 {
def addFoo(id: Int): Int = id + 0xF00
def getUser(id: Int) = User(id)
}
object Source3 {
def getGoodInt = 0xC0FFEE
}
// Finally, our dark implicit magic starts
// This type will provide a way to give requested result for provided parameter
// and sealedness will prevent user from adding more sources - remove if not needed
sealed trait CanGive[Param, Result] {
def apply(p: Param): Result
}
// Scala will look for implicit CanGive-s in companion object
object CanGive {
private def wrap[P, R](fn: P => R): P CanGive R =
new (P CanGive R) {
override def apply(p: P): R = fn(p)
}
// there three show how you can pass your Context here. I'm using DummyImplicits here as placeholders
implicit def param1ToInt(implicit source: DummyImplicit): CanGive[Param1.type, Int] =
wrap((p: Param1.type) => Source1.getInt)
implicit def param2ToInt(implicit source: DummyImplicit): CanGive[Param2, Int] =
wrap((p: Param2) => Source2.addFoo(p.id))
implicit def param2ToUser(implicit source: DummyImplicit): CanGive[Param2, User] =
wrap((p: Param2) => Source2.getUser(p.id))
implicit val param3ToInt: CanGive[Param3, Int] = wrap((p: Param3) => Source3.getGoodInt)
// This one is completely ad-hoc and doesn't even use the Source3, only parameter
implicit val param3ToString: CanGive[Param3, String] = wrap((p: Param3) => p.filter.map(p.key + ":" + _).getOrElse(p.key))
}
object MyApi {
// We need a get method with two generic parameters: Result type and Parameter type
// We can "curry" type parameters using intermediate class and give it syntax of a function
// by implementing apply method
def get[T] = new _GetImpl[T]
class _GetImpl[Result] {
def apply[Param](p: Param)(implicit ev: Param CanGive Result): Result = ev(p)
}
}
MyApi.get[Int](Param1) // 42: Int
MyApi.get[Int](Param2(5)) // 3845: Int
MyApi.get[User](Param2(1)) // User(1): User
MyApi.get[Int](Param3("Foo", None)) // 12648430: Int
MyApi.get[String](Param3("Text", Some(" read it"))) // Text: read it: String
// The following block doesn't compile
//MyApi.get[User](Param1) // Implicit not found
//MyApi.get[String](Param1) // Implicit not found
//MyApi.get[User](Param3("Slevin", None)) // Implicit not found
//MyApi.get[System](Param2(1)) // Same. Unrelated requested types won't work either
object Main extends App {
sealed trait Parameter
case class Parameter1(n: Int) extends Parameter with Source[Int] {
override def get(p: Parameter): MyData[Int] = MyData(n)
}
case class Parameter2(s: String) extends Parameter with Source[String] {
override def get(p: Parameter): MyData[String] = MyData(s)
}
case class MyData[T](t: T)
trait Source[T] {
def get(p: Parameter): MyData[T]
}
object MyAPI {
def get[T](p: Parameter with Source[T]): MyData[T] = p match {
case p1: Parameter1 => p1.get(p)
case p2: Parameter2 => p2.get(p)
}
}
val data1: MyData[Int] = MyAPI.get(Parameter1(15)) // this should give MyData from Source1 of type T1
val data2: MyData[String] = MyAPI.get(Parameter2("Hello World")) // this should give MyData from Source3 of type T2
println(data1)
println(data2)
}
Does this do what you want?
ScalaFiddle: https://scalafiddle.io/sf/FrjJz75/0
I'm making a Scala app that sets by reflection field values. This works OK.
However, in order to set field values I need a created instance. If I have a class with an empty constructor, I can do this easily with classOf[Person].getConstructors....
However, when I try doing this with a Case class with a non empty constructor It doesn't work. I have all of the field names and its values, as well as the Object type I need to create. Can I instance the Case Class somehow with what I've got?
The only thing I don't have is the parameter names from the Case Class constructor or a way to create this without parameter and then setting the values via reflection.
Let's go to the example.
I have the following
case class Person(name : String, age : Int)
class Dog(name : String) {
def this() = {
name = "Tony"
}
}
class Reflector[O](obj : O) {
def setValue[F](propName : String, value : F) = ...
def getValue(propName : String) = ...
}
//This works
val dog = classOf[Dog].newInstance()
new Reflector(dog).setValue("name", "Doggy")
//This doesn't
val person = classOf[Person].newInstance //Doesn't work
val ctor = classOf[Person].getConstructors()(0)
val ctor.newInstance(parameters) //I have the property names and values, but I don't know
// which of them is for each parameter, nor I name the name of the constructor parameters
If you are looking for a way to instantiate the object with no arguments, you could do the same as you did in your example, just so long as your reflection setter can handle setting the immutable vals.
You would provide an alternate constructor, as below:
case class Person(name : String, age : Int) {
def this() = this("", 0)
}
Note that the case class will not generate a zero-arg companion object, so you will need to instantiate it as: new Person() or classOf[Person].newInstance(). However, that should be what you are looking to do.
Should give you output like:
scala> case class Person(name : String, age : Int) {
| def this() = this("", 0)
| }
defined class Person
scala> classOf[Person].newInstance()
res3: Person = Person(,0)
The case class should have default args, so that you can just Person(); in the absence of a default arg, supplying a null for name might (or ought to) hit a require(name != null).
Alternatively, use reflection to figure out which params have defaults and then supply nulls or zeros for the rest.
import reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
// case class instance with default args
// Persons entering this site must be 18 or older, so assume that
case class Person(name: String, age: Int = 18) {
require(age >= 18)
}
object Test extends App {
// Person may have some default args, or not.
// normally, must Person(name = "Guy")
// we will Person(null, 18)
def newCase[A]()(implicit t: ClassTag[A]): A = {
val claas = cm classSymbol t.runtimeClass
val modul = claas.companionSymbol.asModule
val im = cm reflect (cm reflectModule modul).instance
defaut[A](im, "apply")
}
def defaut[A](im: InstanceMirror, name: String): A = {
val at = newTermName(name)
val ts = im.symbol.typeSignature
val method = (ts member at).asMethod
// either defarg or default val for type of p
def valueFor(p: Symbol, i: Int): Any = {
val defarg = ts member newTermName(s"$name$$default$$${i+1}")
if (defarg != NoSymbol) {
println(s"default $defarg")
(im reflectMethod defarg.asMethod)()
} else {
println(s"def val for $p")
p.typeSignature match {
case t if t =:= typeOf[String] => null
case t if t =:= typeOf[Int] => 0
case x => throw new IllegalArgumentException(x.toString)
}
}
}
val args = (for (ps <- method.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
(im reflectMethod method)(args: _*).asInstanceOf[A]
}
assert(Person(name = null) == newCase[Person]())
}
The approach below works for any Scala class that has either a no-arg ctor or has an all defaulted primary ctor.
It makes fewer assumptions than some others about how much information is available at the point of call as all it needs is a Class[_] instance and not implicits etc. Also the approach does not rely on the class having to be a case class or having a companion at all.
FYI During construction precedence is given to the no-arg ctor if present.
object ClassUtil {
def newInstance(cz: Class[_ <: AnyRef]): AnyRef = {
val bestCtor = findNoArgOrPrimaryCtor(cz)
val defaultValues = getCtorDefaultArgs(cz, bestCtor)
bestCtor.newInstance(defaultValues: _*).asInstanceOf[A]
}
private def defaultValueInitFieldName(i: Int): String = s"$$lessinit$$greater$$default$$${i + 1}"
private def findNoArgOrPrimaryCtor(cz: Class[_]): Constructor[_] = {
val ctors = cz.getConstructors.sortBy(_.getParameterTypes.size)
if (ctors.head.getParameterTypes.size == 0) {
// use no arg ctor
ctors.head
} else {
// use primary ctor
ctors.reverse.head
}
}
private def getCtorDefaultArgs(cz: Class[_], ctor: Constructor[_]): Array[AnyRef] = {
val defaultValueMethodNames = ctor.getParameterTypes.zipWithIndex.map {
valIndex => defaultValueInitFieldName(valIndex._2)
}
try {
defaultValueMethodNames.map(cz.getMethod(_).invoke(null))
} catch {
case ex: NoSuchMethodException =>
throw new InstantiationException(s"$cz must have a no arg constructor or all args must be defaulted")
}
}
}
I ran into a similar problem. Given the ease of using Macro Paradise, Macro Annotations are a solution (for scala 2.10.X and 2.11 so far).
Check out this question and the example project linked in the comments below.
I am trying to write a generic method f[T](id:String) that is something like this:
case class A(x:String)
case class B(y:String)
case class C(z:String)
def f[T](id:String): T = { /* equivalent to T(id) */ }
val result1:A = f[A]("123") // returns A("123")
val result2:B = f{B]("345") // returns B("345")
val result3:C = f[C]("567") // returns C("567")
Unfortunately I cannot figure out how to work with the type T inside the method, besides using reflection. By "working with the type T" i mean for example being able to do something like the following, which I know doesn't work (for illustration purposes only):
T match {
case A => A(id)
case B => B(id)
}
or simply invoke T(ID) to create a new object of whatever type T is.
I can of course break up this into three methods:
def f1(id:String): A = { A(id) }
def f2(id:String): B = { B(id) }
def f3(id:String): C = { C(id) }
val result1:A = f1("123") // returns A("123")
val result2:B = f2("345") // returns B("345")
val result3:C = f3("567") // returns C("567")
but I'm hoping there is a way to keep it as one generic method to avoid some ugly boilerplate code duplication, and still be nearl as fast as the tree method version.
If you do not want to use reflection (ClassTag or TypeTag), you could use a Factory type class to achieve the desired functionality (unless it defeats the purpose of your generic function by generating a lot of duplicated simple code ;)).
case class A(s: String)
case class B(s: String)
case class C(s: String)
trait Factory[T] extends ((String) => T) {
def apply(arg: String): T
}
object Factory {
implicit object AFactory extends Factory[A] {
override def apply(arg: String): A = A(arg)
}
implicit object BFactory extends Factory[B] {
override def apply(arg: String): B = B(arg)
}
implicit object CFactory extends Factory[C] {
override def apply(arg: String): C = C(arg)
}
}
def create[T : Factory](arg: String): T = implicitly[Factory[T]].apply(arg)
create[A]("foo") | -> res0: A = A(foo)