Scala implicit type class dependency injection - scala

I'd like some help sorting out this scenario. I have an Akka actor where I want to inject a dependency, in this case RemoteFetcher, which I would also like mock in my tests. Like so:
main/src/scala/mypackage/Services.scala
package mypackage
import RemoteFetcherFileSystem._
trait RemoteFetcher {
def fetch( path:String ): Future[Stream[String]]
}
class MyRemoteResourceActor extends Actor with ActorLogging {
def fetchRemote( path:String ) = implicitly[RemoteFetcher].fetch( path )
def receive = {
case FetchRemoteResource( path ) => fetchRemote( path ).map( _.foreach( sender ! _ ) )
}
}
For this to work I have an implicit object that I import into the file above. Would look something like this:
implicit object RemoteFetcherFileSystem extends RemoteFetcher {
def fetchRemote( path:String ) = Future[Stream[String]] { ... reading from file system ... }
}
Now in my tests I have TestActor from the akka-testkit. Here I want to instead import my mock dependency:
implicit object RemoteFetcherMock extends RemoteFetcher {
def fetchRemote( path:String ) = Future[Stream[String]] { ... mock implementation ... }
}
My problem is that to compile Services.scala I need to import the implicit object. But how do I go about to shadow/override this in my test-files. The reason I'm not using implicit arguments is that I want to avoid having to modify all my actors constructor arguments.
I when looking around and reading up on the type class dependency injection pattern and I get it to work according to the tutorials, but I don't get it to work when I want to test and override like in my example.

I'm not sure how to do it with implicits, but typically one could inject instead like so:
trait RemoteFetcherComponent {
def remoteFetcher: RemoteFetcher
trait RemoteFetcher {
def fetch(path: String): Future[Stream[String]]
}
}
trait RemoteFetcherFileSystemComponent extends RemoteFetcherComponent {
val remoteFetcher = RemoteFetcherFileSystem
object RemoteFetcherFileSystem extends RemoteFetcher {
def fetch(path: String): Future[Stream[String]] = ???
}
}
class MyRemoteResourceActor extends Actor with ActorLogging with RemoteFetcherFileSystemComponent {
def fetchRemote(path: String) = remoteFetcher.fetch(path)
def receive = {
case FetchRemoteResource(path) => fetchRemote(path).map( _.foreach(sender ! _))
}
}
val myRemoteResourceActor = new MyRemoteResourceActor()
And then a test value would be defined like so:
trait RemoteFetcherMockComponent extends RemoteFetcherComponent {
def remoteFetcher = RemoteFetcherMock
object RemoteFetcherMock extends RemoteFetcher {
def fetch(path: String): Future[Stream[String]] = ???
}
}
val myMockedResourceActor = new MyRemoteResourceActor with RemoteFetcherMockComponent {
override val remoteFetcher = super[RemoteFetcherMockComponent].remoteFetcher
}
The reason you are having an issue with implicits is because the way you're using it is no different from simply using def fetchRemote(path: String) = RemoteFetcherFileSystem.fetch(path). With the import, you've defined the implementation, rather than allowed it to be injected later.

You could also change the implicitly to an implicit parameter:
trait RemoteFetcher {
def fetch(path: String): Future[Stream[String]]
}
object RemoteFetcher {
implicit val fetcher = RemoteFetcherFileSystem
}
class MyRemoteResourceActor extends Actor with ActorLogging {
def fetchRemote(path: String)(implicit remoteFetcher: RemoteFetcher) = remoteFetcher.fetch(path)
def receive = {
case FetchRemoteResource(path) => fetchRemote(path).map( _.foreach(sender ! _))
}
}
Then you could override the implicit that is resolved in the companion object of RemoteFetcher by simply importing RemoteFetcherMock.
See this post for more information about implicit parameter resolution precedence rules.

Related

using enumeratum enum as BSONDocument value does not compile

when I try to wrap my query in BSONDocument and putting my enumeratum enum as the value it docent compile.
for example, my enum:
sealed trait ProcessingStatus extends EnumEntry with UpperSnakecase
object ProcessingStatus extends Enum[ProcessingStatus] with ReactiveMongoBsonEnum[ProcessingStatus] {
val values: IndexedSeq[ProcessingStatus] = findValues
case object Processing extends ProcessingStatus
case object Done extends ProcessingStatus
}
and I have play json serializer that explains how to serialize:
object JsonSerialization {
import reactivemongo.api.bson._
implicit object ProcessingStatusReader extends BSONReader[ProcessingStatus] {
override def readTry(bson: BSONValue): Try[ProcessingStatus] = bson match {
case BSONString(s) => bson.asTry[ProcessingStatus]
case _ => Failure(new RuntimeException("String value expected"))
}
}
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
//Report Serializers
implicit val ProcessingStatusFormat: Format[ProcessingStatus] = EnumFormats.formats(ProcessingStatus)
implicit val ReportFormat: OFormat[Report] = Json.format[Report]
}
and now in my dao this does not compile:
import reactivemongo.play.json.compat.json2bson.{toDocumentReader, toDocumentWriter}
import serializers.JsonSerialization._
def findReport(reportId: String) = {
val test = BSONDocument("123" -> ProcessingStatus.Processing) // dosent compile
}
screenshot:
compilation error:
overloaded method apply with alternatives:
(elms: Iterable[(String, reactivemongo.api.bson.BSONValue)])reactivemongo.api.bson.BSONDocument <and>
(elms: reactivemongo.api.bson.ElementProducer*)reactivemongo.api.bson.BSONDocument
cannot be applied to ((String, enums.ProcessingStatus.Done.type))
val test = BSONDocument("status" -> ProcessingStatus.Done)
An IDE error is not a compilation error (recommend to use sbt and its console to tests).
Your code (simplified as bellow), is compiling fine, whatever is telling the IDE (which is wrong).
import reactivemongo.api.bson._
import scala.util.Try
trait ProcessingStatus {
def entryName = "foo"
}
object JsonSerialization {
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
}
import JsonSerialization._
BSON.write(new ProcessingStatus {})
Note.1: writeTry doesn't override anything, so the modifier is useless (and can lead to missunderstanding).
Note.2: Try(..) with a pure value such as BSONString(t.entryName) is over-engineered, rather use Success(..).
Note.3: Convenient factories are available such as val w = BSONWriter[T] { t => ... }.
Edit:
The typeclass BSONWriter (as most typeclass) is invariant, so having a BSONWriter[T] in the implicit scope doesn't allow to resolve a BSONWriter[U] forSome { U <: T }.
trait ProcessingStatus {
def entryName: String
}
object ProcessingStatus {
case object Done extends ProcessingStatus { val entryName = "done" }
}
object JsonSerialization {
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
}
import JsonSerialization._
BSON.write(ProcessingStatus.Done
/*
<console>:32: error: could not find implicit value for parameter writer: reactivemongo.api.bson.BSONWriter[ProcessingStatus.Done.type]
BSON.write(ProcessingStatus.Done)
*/
// --- BUT ---
BSON.write(ProcessingStatus.Done: ProcessingStatus)
// Success(BSONString(done))
Also exposing Done (and other cases) as ProcessingStatus in the API is working.
import reactivemongo.api.bson._
import scala.util.Try
sealed trait ProcessingStatus {
def entryName: String
}
object ProcessingStatus {
val Done: ProcessingStatus = new ProcessingStatus { val entryName = "done" }
}
object JsonSerialization {
implicit object ProcessingStatusWriter extends BSONWriter[ProcessingStatus] {
override def writeTry(t: ProcessingStatus): Try[BSONString] = Try(BSONString(t.entryName))
}
}
import JsonSerialization._
BSON.write(ProcessingStatus.Done)

How to override an implicit value, that is imported?

I have tried solutions, described in How to override an implicit value?, but it does not help. Here is a code example.
A definition of TestImplicit abstraction with 2 different implementations (analogue of ExecutionContextExecutor):
trait TestImplicit {
def f(s:String):Unit
}
object TestImplicitImpl1 extends TestImplicit {
override def f(s: String): Unit = println(s"1: $s")
}
object TestImplicitImpl2 extends TestImplicit {
override def f(s: String): Unit = println(s"2: $s")
}
And in the ImplDefinition object a q variable is defined to be used implicitly via import (analogue of ExecutionContext.Implicits.global):
object ImplDefinition {
implicit val q:TestImplicit = TestImplicitImpl1
}
Client that defines a method, accepting TestImplicit implicitly (analogue of scala.concurrent.Future):
trait TestImplicitClient {
def fu(implicit ti:TestImplicit):Unit
}
object TestImplicitClient extends TestImplicitClient {
override def fu(implicit ti: TestImplicit): Unit = {
println("client")
ti.f("param")
}
}
The next step, a client of client, that chooses which implementation of TestImplicit should be used, the decision is done via import (analogue of API that uses Future):
object ClientOfClient {
import somepackage.ImplDefinition.q
def t():Unit =
TestImplicitClient.fu
}
Now in test, I want to use this ClientOfClient.t(), but I need to override implicit, and use TestImplicitImpl2 instead. The main idea behind - implicits should be defined/overridable by the client of API, but not by API itself:
import somepackage.{ClientOfClient, TestImplicit, TestImplicitImpl2}
import org.junit.Test
class ImplTest {
// trying to hide it via import, does not help
import somepackage.ImplDefinition.{q => _,_}
#Test def test(): Unit ={
//trying to hide it via downgrading to non-implicit, does not work either
val q = somepackage.ImplDefinition.q
implicit val ti = TestImplicitImpl2
ClientOfClient.t()
}
}
Each time I run test, I get in the output:
client
1: param
But not expected:
client
2: param
How can I fix it? I need a way to allow clients to override implicits and stay with as simple API as possible. Which means, I do not want to add additional implicit parameter into ClientOfClient.t() method.
As soon as you have a singleton object ClientOfClient with a hard-coded constant TestImplicitImpl1 everywhere, there is essentially nothing you can do. But there are several work-arounds.
1. Use default implicit parameters
object ClientOfClient {
def t()(implicit ti: TestImplicit = ImplDefinition.q): Unit =
TestImplicitClient.fu
}
object ImplTest {
def test(): Unit = {
implicit val ti2 = TestImplicitImpl2
ClientOfClient.t()
}
}
ImplTest.test() // 2: param
2. Supply the implicit through a separate method that can be overridden
If you want to make the implicit overridable, then make ClientOfClient extendable, and create a method (here "cocti") that returns the implicit, instead of importing the implicit directly. You can then override the method (whereas you cannot override an import).
This here produces 2: param in the end of the day:
trait TestImplicit {
def f(s: String): Unit
}
object TestImplicitImpl1 extends TestImplicit {
override def f(s: String): Unit = println(s"1: $s")
}
object TestImplicitImpl2 extends TestImplicit {
override def f(s: String): Unit = println(s"2: $s")
}
object ImplDefinition {
implicit val q: TestImplicit = TestImplicitImpl1
}
trait TestImplicitClient {
def fu(implicit ti: TestImplicit): Unit
}
object TestImplicitClient extends TestImplicitClient {
override def fu(implicit ti: TestImplicit): Unit = {
println("client")
ti.f("param")
}
}
class ClientOfClient {
implicit def cocti: TestImplicit = {
ImplDefinition.q
}
def t():Unit =
TestImplicitClient.fu
}
object ImplTest {
def test(): Unit = {
implicit val ti2 = TestImplicitImpl2
new ClientOfClient {
override def cocti = ti2
}.t()
}
}
ImplTest.test() // 2: param

Scala type class can't find implicit instances

I am trying to create a type class in Scala and use it to make a simple polymorphic case class. This example doesn't compile and gives "could not find implicit value for parameter writer:A$A228.this.ValueWriter[T]". I can't really figure out what could be going wrong or where to start.
trait Keeper
case class StringKeeper(measure: String) extends Keeper
case class StringLengthKeeper(measure: Int) extends Keeper
trait ValueWriter[A] {
def write(value: String): A
}
object DefaultValueWriters {
implicit val stringWriter = new ValueWriter[StringKeeper] {
def write(value: String) = StringKeeper(value)
}
implicit val stringLengthWriter = new ValueWriter[StringLengthKeeper] {
def write(value: String) = StringLengthKeeper(value.length)
}
}
object Write {
def toWrite[A](value: String)(implicit writer: ValueWriter[A]) = {
writer.write(value)
}
}
case class WriterOfKeepers[T <: Keeper](value: String) {
def run: T = {
Write.toWrite[T](value)
}
}
import DefaultValueWriters._
val writerLengthKeeper = WriterOfKeepers[StringLengthKeeper]("TestString")
writerLengthKeeper.run
There is no implicit ValueWriter in scope when you call Write.toWrite.
You need to have the implicit parameter in the constructor
case class WriterOfKeepers[T <: Keeper : ValueWriter](value: String) {
def run: T = {
Write.toWrite[T](value)
}
}
or in the method
case class WriterOfKeepers[T <: Keeper](value: String) {
def run(implicit writer: ValueWriter[T]): T = {
Write.toWrite(value)
}
}
or find some other way compatible with your requirements (and I don't know those).

Spray: Overriding functions in Traits

I'm using Spray and have a Base Route trait which defines a number of functions that should be implemented...
trait ServiceBaseRoute extends HttpService {
def function1():Type
def function2():Type
lazy val serviceBaseRoute = ...
I then mixin this trait to a number of other traits...
trait MyRoute1 extends HttpService
with ServiceBaseRoute {
override def function1():Type = {...}
override def function2():Type = {...}
val myRoute1 = serviceBaseRoute
and...
trait MyRoute2 extends HttpService
with ServiceBaseRoute {
override def function1():Type = {...}
override def function2():Type = {...}
val myRoute2 = serviceBaseRoute
Finally I construct the top level of the route as follows...
trait V1Routes extends HttpService
with MyRoute1
with MyRoute2 {
val v1Routes =
pathPrefix("v1") {
authenticate(...) {
myRoute1 ~ myRoute2
}
}
Compiles fine etc... However when I run this the function overrides of MyRoute2 override those defined in MyRoute1. I am thinking this is because I don't have actual instances of MyRoute1 and MyRoute2 as they are being simply mixed in and since MyRoute2 is added in after MyRoute1 its values overwrite those of MyRoute1?
So what would be the best way to implement this preserving the overrides defined in each trait?
Do I need to define MyRoute1 and MyRoute2 as objects instead?
Thanks
Based on your comments, the aim is to have distinct routes in MyRoute1 and MyRoute2, where each is created by customizing serviceBaseRoute. Since ServiceBaseRoute is customized by two functions, you could make serviceBaseRoute a higher order function and pass the implementations of the two functions to it in the sub traits:
trait ServiceBaseRoute extends HttpService {
def serviceBaseRoute(f1: () => Type, f2: () => Type)= ???
}
trait MyRoute1 extends HttpService
with ServiceBaseRoute {
def f1ImplA(): Type = ???
def f2ImplA(): Type = ???
val myRoute1 = serviceBaseRoute(f1ImplA, f2ImplA)
}
trait MyRoute2 extends HttpService
with ServiceBaseRoute {
def f1ImplB(): Type = ???
def f2ImplB(): Type = ???
val myRoute2 = serviceBaseRoute(f1ImplB, f2ImplB)
}
trait V1Routes extends HttpService
with MyRoute1
with MyRoute2 {
val v1Routes =
pathPrefix("v1") {
authenticate(...)
{
myRoute1 ~ myRoute2
}
}
}

Using trait method in the class constructor

I have a trait and a class that extends the trait. I can use the methods from the trait as follows:
trait A {
def a = ""
}
class B(s: String) extends A {
def b = a
}
However, when I use the trait's method in the constructor like this:
trait A {
def a = ""
}
class B(s: String) extends A {
def this() = this(a)
}
then the following error appears:
error: not found: value a
Is there some way to define default parameters for the construction of classes in the trait?
EDIT: To clarify the purpose: There is the akka-testkit:
class TestKit(_system: ActorSystem) extends { implicit val system = _system }
And each test looks like this:
class B(_system: ActorSystem) extends TestKit(_system) with A with ... {
def this() = this(actorSystem)
...
}
because I want to create common creation of the ActorSystem in A:
trait A {
val conf = ...
def actorSystem = ActorSystem("MySpec", conf)
...
}
It's a little bit tricky because of Scala initialization order. The simplest solution I found is to define a companion object for your class B with apply as factory method:
trait A {
def a = "aaaa"
}
class B(s: String) {
println(s)
}
object B extends A {
def apply() = new B(a)
def apply(s: String) = new B(s)
}