Macwire, wireWith and implicit parameters - scala

wireWith seems to have some issues with resolving implicit parameters.
Minimal example:
import com.softwaremill.macwire._
object A {
def props(x: Int)(implicit y: String): A = new A(x)
}
class A(x: Int)(implicit y: String) {
val sum: String = s"$x + $y"
}
object App {
def main(): Unit = {
val xVal: Int = 3
implicit val yVal: String = "5"
// val aInstance = wire[A] // works
// val aInstance = A.props(xVal) // works
val aInstance: A = wireWith(A.props _) // compile error
println(aInstance.sum)
}
}
App.main()
Error:
Error:(21, 33) type mismatch; found : Int required: String
val aInstance: A = wireWith(A.props _)
^
If implicit is ommitted at yVal, it complains about missing implicit:
Error:(18, 36) could not find implicit value for parameter y: String
val aInstance: A = wireWith(A.props _) // compile error
^
This is a simplified example though - in my production code i try to wire Actor props:
object Actor {
def props
(dependency: Dependency, refreshInterval: FiniteDuration ## CacheRefreshInterval)
(implicit ec: ExecutionContext): Props =
Props(new Actor(dependency, refreshInterval))
}
// DI container
protected implicit def executionContext: ExecutionContext
protected lazy val dependency: Dependency = wire[Dependency]
protected lazy val refreshInterval = 2.second.taggedWith[CacheRefreshInterval]
protected lazy val actorProps: Props ## ActorProps = actorwireWith(Actor.props _)
and get different compile error:
too many arguments for method props: (implicit ec: scala.concurrent.ExecutionContext)akka.actor.Props
I've tried making implicit parameter explicit and it worked just fine, except it's a bit against usual practice of passing execution context implicitly.
Am I doing something wrong or is it an issue in macwire?
Issue in Github: https://github.com/adamw/macwire/issues/125

If you were using wireWith(A.props _) as a workaround for macwire's inability to wire actors when their constructor are using implicit parameters (see macwire issue 121), you could switch to macwire 2.3.1 and wire your actors using wireActor[A], wireAnonymousActor[A] or wireProps[A]. issue 121 was fixed in macwire 2.3.1.

Related

Why does flatten on nested Iterator not compile and why do I need type ascription?

(new Iterator[List[Int]] {
def hasNext: Boolean = ???
def next(): List[Int] = ???
}).flatten
gives error:
value flatten is not a member of Iterator[List[Int]]
[error] possible cause: maybe a semicolon is missing before `value flatten'?
[error] }.flatten
[error] ^
[error] one error found
But
(new Iterator[List[Int]] {
def hasNext: Boolean = ???
def next(): List[Int] = ???
}: Iterator[List[Int]]).flatten
works. Also storing the iterator in a val works.
Scala version: 2.11.8
I believe this issue was addressed by Include the parts of a compound/refinement type in implicit scope. #5867. The problem was companion to anonymous classes used to not qualify for implicit search
I think aaf9198#diff-7c03397456d3b987549fd039d6b639c6R516 was the
first to exclude refinement/anon classes from contributing to
companion implicits. #odersky Can you remember the motivation?
Here is a minimal reproduction
trait A {
def foo(): Int
}
class B {
def bar(): String = "woohoo"
}
object A {
implicit def aToB(a: A): B = new B
}
(new A {
override def foo(): Int = 42
}).bar()
// Error:
// value bar is not a member of A$A12.this.A
// possible cause: maybe a semicolon is missing before `value bar'? }).bar();
Therefore since in 2.11 and 2.12 flatten on Iterator was provided as an extension method via flattenTraversableOnce the issue would manifest for anonymous Iterator class.
The following is my answer prior to the edit above:
It seems to have something to do with implicit resolution in both 2.11 and 2.12. If you explicitly import flatten as an extension method via
import scala.collection.TraversableOnce.flattenTraversableOnce
then it seems to work. The issue seems to be fixed since 2.13.0-M3 where typer phase gives
collection.this.TraversableOnce.flattenTraversableOnce[Int, List]({
final class $anon extends AnyRef with Iterator[List[Int]] {
def <init>(): <$anon: Iterator[List[Int]]> = {
$anon.super.<init>();
()
};
def hasNext: Boolean = scala.Predef.???;
def next(): List[Int] = scala.Predef.???
};
new $anon()
})(scala.Predef.$conforms[List[Int]]).flatten
whilst in 2.13.0 release flatten no longer seem to be provided via extension method
{
final class $anon extends AnyRef with Iterator[List[Int]] {
def <init>(): <$anon: Iterator[List[Int]]> = {
$anon.super.<init>();
()
};
def hasNext: Boolean = scala.Predef.???;
def next(): List[Int] = scala.Predef.???
};
new $anon()
}.flatten[Int](scala.Predef.$conforms[List[Int]])
The above expansion seems to be explained by SLS 6.4 Designators
𝑒.𝑥 is typed as if it was { val 𝑦 = 𝑒; 𝑦.𝑥 }
I think it tries to parse this:
new Iterator[List[Int]] {
def hasNext: Boolean = ???
def next(): List[Int] = ???
}.flatten
as this
new Iterator[List[Int]] ( {
def hasNext: Boolean = ???
def next(): List[Int] = ???
}.flatten )
Notice where I added the parentheses ().

How to support marshalling and unmarshalling in Akka HTTP with JSON Spray for HashMap fields?

I have the following case classes:
import scala.collection.immutable.HashMap
final case class GuiApplication(testSuites: TestSuites)
final case class GuiApplications(var applications: HashMap[Id, GuiApplication])
final case class Id(val id: Long)
final case class TestSuite()
final case class TestSuites(var testSuites: HashMap[Id, TestSuite])
The marshalling defines:
implicit val applicationsMapFormat = jsonFormat0(HashMap[Id, GuiApplication])
implicit val testsuitesMapFormat = jsonFormat0(HashMap[Id, TestSuite])
implicit val idFormat = jsonFormat1(Id)
implicit val testSuiteFormat = jsonFormat0(TestSuite)
implicit val testSuitesFormat = jsonFormat1(TestSuites)
implicit val applicationFormat = jsonFormat1(GuiApplication)
implicit val applicationsFormat = jsonFormat1(GuiApplications)
But I still get these compile errors:
type mismatch;
[error] found : Seq[(Id, GuiApplication)] => scala.collection.immutable.HashMap[Id,GuiApplication]
[error] required: () => ?
[error] Note: implicit value applicationsMapFormat is not applicable here because it comes after the application point and it lacks an explicit result type
[error] implicit val applicationsMapFormat = jsonFormat0(HashMap[Id, GuiApplication])
[error] ^
If I remove the first two implicits I get other compile errors.
How can I define implicit JSON formats for map types in Scala?
You just need to write your custom RootJsonFormat[scala.collection.immutable.HashMap[Id,GuiApplication]] and add it to the scope of your routes:
implicit val hashMapFormat: RootJsonFormat[scala.collection.immutable.HashMap[Id,GuiApplication]] = new RootJsonFormat[scala.collection.immutable.HashMap[Id,GuiApplication]] {
override def write(obj: scala.collection.immutable.HashMap[Long, String]): JsValue = ???
override def read(json: JsValue): scala.collection.immutable.HashMap[Id,GuiApplication] = ???
}

Scala compile error - could not find implicit value for evidence parameter of type

I am using spray json serialization for my following case class
case class ActivationMessage(override val transid: TransactionId,
action: FullyQualifiedEntityName,
revision: DocRevision,
user: Identity,
activationId: ActivationId,
activationNamespace: EntityPath,
content: Option[JsObject],
cause: Option[ActivationId] = None,
traceMetadata: Option[SpanMetadata] = None
) extends Message {
def meta = JsObject("meta" -> {
cause map {
c => JsObject(c.toJsObject.fields ++ activationId.toJsObject.fields)
} getOrElse {
activationId.toJsObject
}
})
override def serialize = ActivationMessage.serdes.write(this).compactPrint
override def toString = {
val value = (content getOrElse JsObject()).compactPrint
s"$action?message=$value"
}
def causedBySequence: Boolean = cause.isDefined
}
object ActivationMessage extends DefaultJsonProtocol {
def parse(msg: String) = Try(serdes.read(msg.parseJson))
private implicit val fqnSerdes = FullyQualifiedEntityName.serdes
implicit val serdes = jsonFormat9(ActivationMessage.apply)
}
I get compilation error saying - could not find implicit value for evidence parameter of type ActivationMessage.JF[Option[com.github.levkhomich.akka.tracing.SpanMetadata]]
If I remove last argument from constructor and use jsonFormat8 every thing compiles fine. How can I add this extra argument without any compile issues?
The compiler cannot find implicit JsonFormat for SpanMetadata. Try:
...
object ActivationMessage extends DefaultJsonProtocol {
...
private implicit val spanMetaSerdes = jsonFormat4(SpanMetadata.apply)
implicit val serdes = jsonFormat9(ActivationMessage.apply)
}

Implicit parameters cannot be found when imported (from an Object)

Why can not the code below find the imported implicits from MyProducers? In my understanding this should work fine because the implicits are in scope.
Error:(16, 34) could not find implicit value for evidence parameter of type MyProducers.ListProducer[Int]
val stuffInt:Int = getHead[Int]()
Error:(16, 34) not enough arguments for method getHead: (implicit evidence$2: MyProducers.ListProducer[Int])Int.
Unspecified value parameter evidence$2.
val stuffInt:Int = getHead[Int]()
Error:(18, 43) could not find implicit value for evidence parameter of type MyProducers.ListProducer[String]
val stuffString:String = getHead[String]()
Error:(18, 43) not enough arguments for method getHead: (implicit evidence$2: MyProducers.ListProducer[String])String.
Unspecified value parameter evidence$2.
val stuffString:String = getHead[String]()
Code:
object Resolver {
import MyProducers._
import MyProducers._
def getList[T:ListProducer]():List[T]= implicitly[ListProducer[T]].produceList
def getHead[T:ListProducer]():T= getList[T]().head
val stuffInt:Int = getHead[Int]()
val stuffString:String = getHead[String]()
val im=ip // this compiles fine, so implicits are in scope
val sm=sp
}
object MyProducers{
trait ListProducer[T]{
def produceList:List[T]
}
object IntProducer extends ListProducer[Int]{
override def produceList = List(22, 42)
}
implicit val ip=IntProducer
object StringProducer extends ListProducer[String]{
override def produceList = List("stuff", "Shiraly")
}
implicit val sp=StringProducer
}
What is strange, that this code compiles fine:
object Resolver {
import MyProducers._
import MyProducers._
trait ListProducer[T]{
def produceList:List[T]
}
object IntProducer extends ListProducer[Int]{
override def produceList = List(22, 42)
}
implicit val ip=IntProducer
object StringProducer extends ListProducer[String]{
override def produceList = List("stuff", "Shiraly")
}
implicit val sp=StringProducer
def getList[T:ListProducer]():List[T]= implicitly[ListProducer[T]].produceList
def getHead[T:ListProducer]():T= getList[T]().head
val stuffInt:Int = getHead[Int]()
val stuffString:String = getHead[String]()
val im=ip
val sm=sp
}
Any idea why the first code does not compile while the second does ?
Implicit vals and defs need to have type annotations on them. Add those, and the implicits will be found.

spray-json can't find JsonReader for type List[T]

I'm creating custom json readers for case classes but it can't find implicit JsonReader type class for List[T] which is used in other case class.
When I checked DefaultJsonProtocol, it has implicit format for collections already;
implicit def listFormat[T :JsonFormat] = new RootJsonFormat[List[T]] {
def write(list: List[T]) = JsArray(list.map(_.toJson).toVector)
def read(value: JsValue): List[T] = value match {
case JsArray(elements) => elements.map(_.convertTo[T])(collection.breakOut)
case x => deserializationError("Expected List as JsArray, but got " + x)
}
}
Here is the simplified code;
case class Test(i: Int, d: Double)
case class ListOfTest(t: List[Test])
trait TestResultFormat extends DefaultJsonProtocol {
import CustomFormat._
implicit object TestJsonFormat extends RootJsonReader[Test] {
override def read(json: JsValue): Test = {
val jsObject = json.asJsObject
val jsFields = jsObject.fields
val i = jsFields.get("i").map(_.convertTo[Int]).getOrElse(0)
val d = jsFields.get("d").map(_.convertTo[Double]).getOrElse(0d)
Test(i, d)
}
}
implicit object ListOfTestJsonFormat extends RootJsonReader[ListOfTest] {
override def read(json: JsValue): ListOfTest = {
val jsObject = json.asJsObject
val jsFields = jsObject.fields
val tests = jsFields.get("hs").map(_.convertTo[List[Test]]).getOrElse(List.empty)
ListOfTest(tests)
}
}
}
Here is the errors;
Error:(230, 53) not enough arguments for method convertTo: (implicit evidence$1: spray.json.JsonReader[List[com.xx.Test]])List[com.xx.Test].
Unspecified value parameter evidence$1.
val tests = jsFields.get("hs").map(_.convertTo[List[Test]]).getOrElse(List.empty)
^
Error:(230, 53) Cannot find JsonReader or JsonFormat type class for List[com.xx.Test]
val tests = jsFields.get("hs").map(_.convertTo[List[Test]]).getOrElse(List.empty)
^
I think the problem is related to the fact that the JsonReader for List[T] in DefaultJsonProtocol is a RootJsonFormat (not a RootJsonReader), which basically means you can read it and also write it. So, when you try to read a List[Item], it's expected that you are also able to write Items. So, you could use RootJsonFormat instead and throw an exception in case you try to write it (since you don't support it). For example:
import spray.json._
implicit object TestJsonFormat extends RootJsonFormat[Test] {
override def read(json: JsValue): Test = {
val jsObject = json.asJsObject
val jsFields = jsObject.fields
val i = jsFields.get("i").map(_.convertTo[Int]).getOrElse(0)
val d = jsFields.get("d").map(_.convertTo[Double]).getOrElse(0d)
Test(i, d)
}
override def write(obj: Test): JsValue = serializationError("not supported")
}
If you are aware of a clean solution involving only the readers, please do let me know because I ran into this problem myself and couldn't find anything else.
I have learned that limitation comes from spray-json:
spray-json 's type class infrastructure is build around the (Root)JsonFormat type, not the (Root)JsonReader. So you'll indeed have to provide a "Format" even if you are just reading.
Check here.
To overcome issue; I created another trait extends RootJsonFormat instead of reader and overrides write method with basically not implemented method.
trait EmptyWriterFormat[T] extends RootJsonFormat[T] {
override def write(o: T): JsValue = ???
}